Skip to content

Commit 8aa8be0

Browse files
committed
reduce optim memory allocation, finish adafactor
1 parent 92f1626 commit 8aa8be0

11 files changed

Lines changed: 180 additions & 283 deletions

File tree

roundpipe/optim/csrc/adadelta.cpp

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,7 @@ void adadelta_kernel(bool current_bool, Args... args) {
3737
void adadelta(vector<Tensor> params, vector<Tensor> grads, vector<Tensor> square_avg,
3838
vector<Tensor> acc_delta, vector<Tensor> state_steps, double lr,
3939
double rho, double eps, double weight_decay, bool maximize) {
40-
vector<int64_t> numel(params.size());
41-
vector<float *> params_ptr(params.size());
42-
vector<const float *> grads_ptr(params.size());
43-
vector<float *> square_avg_ptr(params.size());
44-
vector<float *> acc_delta_ptr(params.size());
4540
for (size_t i = 0; i < params.size(); ++i) {
46-
numel[i] = params[i].numel();
47-
params_ptr[i] = params[i].mutable_data_ptr<float>();
48-
grads_ptr[i] = grads[i].const_data_ptr<float>();
49-
square_avg_ptr[i] = square_avg[i].mutable_data_ptr<float>();
50-
acc_delta_ptr[i] = acc_delta[i].mutable_data_ptr<float>();
5141
// The step is tracked for state-dict compatibility but does not enter the math.
5242
state_steps[i].add_(1);
5343
}
@@ -56,12 +46,16 @@ void adadelta(vector<Tensor> params, vector<Tensor> grads, vector<Tensor> square
5646
int rank = omp_get_thread_num();
5747
int nthreads = omp_get_num_threads();
5848
for (size_t i = 0; i < params.size(); ++i) {
59-
int64_t block_size = numel[i] / nthreads + (rank < (numel[i] % nthreads));
49+
int64_t numel = params[i].numel();
50+
int64_t block_size = numel / nthreads + (rank < (numel % nthreads));
6051
int64_t offset =
61-
(numel[i] / nthreads) * rank + min<int64_t>(rank, numel[i] % nthreads);
62-
adadelta_kernel(maximize, weight_decay == 0.0, params_ptr[i] + offset,
63-
grads_ptr[i] + offset, square_avg_ptr[i] + offset,
64-
acc_delta_ptr[i] + offset, lr, rho, eps, weight_decay,
52+
(numel / nthreads) * rank + min<int64_t>(rank, numel % nthreads);
53+
float *params_ptr = params[i].mutable_data_ptr<float>() + offset;
54+
const float *grads_ptr = grads[i].const_data_ptr<float>() + offset;
55+
float *square_avg_ptr = square_avg[i].mutable_data_ptr<float>() + offset;
56+
float *acc_delta_ptr = acc_delta[i].mutable_data_ptr<float>() + offset;
57+
adadelta_kernel(maximize, weight_decay == 0.0, params_ptr, grads_ptr,
58+
square_avg_ptr, acc_delta_ptr, lr, rho, eps, weight_decay,
6559
block_size);
6660
}
6761
}

roundpipe/optim/csrc/adafactor.cpp

Lines changed: 81 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -17,41 +17,38 @@ using namespace torch;
1717
template <bool maximize, bool zero_weight_decay>
1818
static void adafactor_matrix(float *__restrict param, const float *__restrict grad,
1919
float *__restrict row_var, float *__restrict col_var,
20-
int64_t B, int64_t R, int64_t C, float f_omb2,
21-
double rho_t, float f_eps1, float f_eps1sq, float f_eps2,
22-
float f_d, float f_wd_factor) {
20+
float *__restrict cs, float *__restrict inv_rvm,
21+
float *__restrict upd, int64_t B, int64_t R, int64_t C,
22+
float f_omb2, double rho_t, double eps1, double eps2,
23+
double d, float f_wd_factor) {
2324
const int64_t RC = R * C;
2425
const int64_t numel = B * RC;
25-
if (numel == 0) {
26-
return; // A zero-sized dim leaves the empty parameter untouched.
27-
}
2826
const float gsign = maximize ? -1.0f : 1.0f;
29-
const float f_inv_C = 1.0f / (float)C;
30-
const float f_inv_R = 1.0f / (float)R;
27+
float f_eps1 = eps1;
28+
float f_eps1sq = eps1 * eps1;
29+
const float f_inv_C = 1.0 / C;
30+
const float f_inv_R = 1.0 / R;
3131
const int64_t BR = B * R;
3232
const int64_t BC = B * C;
3333

3434
// --- RMS(param) for the step size, computed before the weight decay. ---
35-
double param_sq = 0.0;
35+
float param_sq = 0.0;
3636
#pragma omp parallel for simd reduction(+ : param_sq) schedule(static)
3737
for (int64_t o = 0; o < numel; ++o) {
38-
param_sq += (double)param[o] * param[o];
38+
param_sq += param[o] * param[o];
3939
}
40-
double rms_param = sqrt(param_sq / (double)numel);
41-
float f_alpha = (float)(max((double)f_eps2, rms_param) * rho_t);
40+
double rms_param = sqrt((double)param_sq / numel);
41+
double alpha = max(eps2, rms_param) * rho_t;
4242

4343
// --- Row sums of grad^2 (and the row_var lerp) in parallel over rows; the
4444
// column sums are a reduction across rows, done with an OpenMP array-section
4545
// reduction so each thread accumulates its own colsum copy. ---
46-
vector<float> colsum((size_t)BC, 0.0f);
47-
float *__restrict cs = colsum.data();
4846
#pragma omp parallel for reduction(+ : cs[ : BC]) schedule(static)
4947
for (int64_t br = 0; br < BR; ++br) {
5048
const int64_t b = br / R;
5149
const float *__restrict grow = grad + br * C;
5250
float *__restrict csb = cs + b * C;
5351
float racc = 0.0f;
54-
#pragma omp simd reduction(+ : racc)
5552
for (int64_t c = 0; c < C; ++c) {
5653
float g2 = grow[c] * grow[c];
5754
racc += g2;
@@ -61,50 +58,51 @@ static void adafactor_matrix(float *__restrict param, const float *__restrict gr
6158
}
6259

6360
// --- col_var lerp (parallel over columns) and the per-matrix mean of row_var
64-
// that normalizes the outer product. B is tiny, so inv_rvm is serial. ---
65-
#pragma omp parallel for simd schedule(static)
66-
for (int64_t bc = 0; bc < BC; ++bc) {
67-
col_var[bc] += f_omb2 * (cs[bc] * f_inv_R - col_var[bc]);
68-
}
69-
vector<float> inv_rvm(B);
70-
for (int64_t b = 0; b < B; ++b) {
71-
const float *__restrict rv = row_var + b * R;
72-
double rv_sum = 0.0;
73-
for (int64_t r = 0; r < R; ++r) {
74-
rv_sum += rv[r];
61+
// that normalizes the outer product. ---
62+
#pragma omp parallel
63+
{
64+
#pragma omp for simd schedule(static) nowait
65+
for (int64_t bc = 0; bc < BC; ++bc) {
66+
col_var[bc] += f_omb2 * (cs[bc] * f_inv_R - col_var[bc]);
67+
}
68+
#pragma omp for schedule(static)
69+
for (int64_t b = 0; b < B; ++b) {
70+
const float *__restrict rv = row_var + b * R;
71+
float rv_sum = 0.0;
72+
#pragma omp simd reduction(+ : rv_sum)
73+
for (int64_t r = 0; r < R; ++r) {
74+
rv_sum += rv[r];
75+
}
76+
inv_rvm[b] = 1.0f / max(rv_sum * f_inv_R, f_eps1);
7577
}
76-
inv_rvm[b] = 1.0f / max((float)(rv_sum * (double)f_inv_R), f_eps1);
7778
}
7879

7980
// --- Build the update U = grad / sqrt(max(var_estimate, eps1^2)) and its RMS,
8081
// parallel over rows. var_estimate[b,r,c] = row_var[b,r] * col_var[b,c] *
8182
// inv_rvm[b].
82-
vector<float> update(numel);
83-
float *__restrict upd = update.data();
84-
double update_sq = 0.0;
83+
float update_sq = 0.0;
8584
#pragma omp parallel for reduction(+ : update_sq) schedule(static)
8685
for (int64_t br = 0; br < BR; ++br) {
8786
const int64_t b = br / R;
8887
float pre = row_var[br] * inv_rvm[b];
8988
const float *__restrict grow = grad + br * C;
9089
const float *__restrict cvb = col_var + b * C;
9190
float *__restrict urow = upd + br * C;
92-
double usq = 0.0;
93-
#pragma omp simd reduction(+ : usq)
91+
float usq = 0.0;
9492
for (int64_t c = 0; c < C; ++c) {
9593
float ve = pre * cvb[c];
9694
ve = ve > f_eps1sq ? ve : f_eps1sq;
9795
float u = gsign * grow[c] / sqrt(ve);
9896
urow[c] = u;
99-
usq += (double)u * u;
97+
usq += u * u;
10098
}
10199
update_sq += usq;
102100
}
103101

104102
// --- Clip by RMS(update)/d and apply the (decoupled) weight decay in one pass. ---
105-
double rms_update = sqrt(update_sq / (double)numel);
106-
float f_denom = (float)max(1.0, rms_update / (double)f_d);
107-
float f_coeff = -f_alpha / f_denom;
103+
double rms_update = sqrt((double)update_sq / numel);
104+
double denom = max(1.0, rms_update / d);
105+
float f_coeff = -alpha / denom;
108106
#pragma omp parallel for simd schedule(static)
109107
for (int64_t o = 0; o < numel; ++o) {
110108
param[o] =
@@ -117,76 +115,54 @@ static void adafactor_matrix(float *__restrict param, const float *__restrict gr
117115
// same elementwise-plus-two-reductions shape as the other RoundPipe optimizers.
118116
template <bool maximize, bool zero_weight_decay>
119117
static void adafactor_vector(float *__restrict param, const float *__restrict grad,
120-
float *__restrict variance, int64_t numel, float f_omb2,
121-
double rho_t, float f_eps1sq, float f_eps2, float f_d,
122-
float f_wd_factor) {
118+
float *__restrict variance, float *__restrict upd,
119+
int64_t numel, float f_omb2, double rho_t, double eps1,
120+
double eps2, double d, float f_wd_factor) {
123121
if (numel == 0) {
124122
return;
125123
}
124+
float f_eps1sq = eps1 * eps1;
126125
const float gsign = maximize ? -1.0f : 1.0f;
127126

128-
double param_sq = 0.0;
129-
#pragma omp parallel for simd reduction(+ : param_sq) schedule(static)
130-
for (int64_t o = 0; o < numel; ++o) {
131-
param_sq += (double)param[o] * param[o];
132-
}
133-
double rms_param = sqrt(param_sq / (double)numel);
134-
float f_alpha = (float)(max((double)f_eps2, rms_param) * rho_t);
135-
136-
vector<float> update(numel);
137-
float *__restrict upd = update.data();
138-
double update_sq = 0.0;
139-
#pragma omp parallel for simd reduction(+ : update_sq) schedule(static)
127+
float param_sq = 0.0, update_sq = 0.0;
128+
#pragma omp parallel for simd reduction(+ : param_sq, update_sq) schedule(static)
140129
for (int64_t o = 0; o < numel; ++o) {
130+
param_sq += param[o] * param[o];
141131
float g = gsign * grad[o];
142132
variance[o] += f_omb2 * (g * g - variance[o]);
143-
float ve = variance[o] > f_eps1sq ? variance[o] : f_eps1sq;
144-
float u = g / sqrt(ve);
133+
float u = g / sqrt(variance[o] > f_eps1sq ? variance[o] : f_eps1sq);
145134
upd[o] = u;
146-
update_sq += (double)u * u;
135+
update_sq += u * u;
147136
}
148137

149-
double rms_update = sqrt(update_sq / (double)numel);
150-
float f_denom = (float)max(1.0, rms_update / (double)f_d);
151-
float f_coeff = -f_alpha / f_denom;
138+
double rms_param = sqrt((double)param_sq / numel);
139+
double alpha = max(eps2, rms_param) * rho_t;
140+
double rms_update = sqrt((double)update_sq / numel);
141+
double denom = max(1.0, rms_update / d);
142+
float f_coeff = -alpha / denom;
152143
#pragma omp parallel for simd schedule(static)
153144
for (int64_t o = 0; o < numel; ++o) {
154145
param[o] =
155146
(zero_weight_decay ? param[o] : param[o] * f_wd_factor) + f_coeff * upd[o];
156147
}
157148
}
158149

159-
// Step every parameter with the maximize / weight-decay branches resolved at
160-
// compile time, matching the templated style of the other RoundPipe kernels.
161-
template <bool maximize, bool zero_weight_decay>
162-
static void adafactor_impl(const vector<float *> &mP, const vector<const float *> &mG,
163-
const vector<float *> &mRV, const vector<float *> &mCV,
164-
const vector<int64_t> &mB, const vector<int64_t> &mR,
165-
const vector<int64_t> &mC, const vector<double> &m_omb2,
166-
const vector<double> &m_rho, const vector<float *> &vP,
167-
const vector<const float *> &vG, const vector<float *> &vVar,
168-
const vector<int64_t> &vN, const vector<double> &v_omb2,
169-
const vector<double> &v_rho, float f_eps1, float f_eps1sq,
170-
float f_eps2, float f_d, float f_wd) {
171-
for (size_t i = 0; i < mP.size(); ++i) {
172-
adafactor_matrix<maximize, zero_weight_decay>(
173-
mP[i], mG[i], mRV[i], mCV[i], mB[i], mR[i], mC[i], (float)m_omb2[i],
174-
m_rho[i], f_eps1, f_eps1sq, f_eps2, f_d, f_wd);
175-
}
176-
for (size_t i = 0; i < vP.size(); ++i) {
177-
adafactor_vector<maximize, zero_weight_decay>(vP[i], vG[i], vVar[i], vN[i],
178-
(float)v_omb2[i], v_rho[i],
179-
f_eps1sq, f_eps2, f_d, f_wd);
150+
// Unpack the runtime booleans into template parameters.
151+
template <bool... FixedBools, typename... Args>
152+
static void adafactor_matrix(bool current_bool, Args &&...args) {
153+
if (current_bool) {
154+
adafactor_matrix<FixedBools..., true>(std::forward<Args>(args)...);
155+
} else {
156+
adafactor_matrix<FixedBools..., false>(std::forward<Args>(args)...);
180157
}
181158
}
182159

183-
// Unpack the runtime booleans into template parameters.
184160
template <bool... FixedBools, typename... Args>
185-
static void adafactor_impl(bool current_bool, Args &&...args) {
161+
static void adafactor_vector(bool current_bool, Args &&...args) {
186162
if (current_bool) {
187-
adafactor_impl<FixedBools..., true>(std::forward<Args>(args)...);
163+
adafactor_vector<FixedBools..., true>(std::forward<Args>(args)...);
188164
} else {
189-
adafactor_impl<FixedBools..., false>(std::forward<Args>(args)...);
165+
adafactor_vector<FixedBools..., false>(std::forward<Args>(args)...);
190166
}
191167
}
192168

@@ -200,52 +176,43 @@ void adafactor(vector<Tensor> params, vector<Tensor> grads, vector<Tensor> row_v
200176
vector<Tensor> col_vars, vector<Tensor> variances,
201177
vector<Tensor> state_steps, double lr, double beta2_decay, double eps1,
202178
double eps2, double d, double weight_decay, bool maximize) {
203-
vector<float *> mP, mRV, mCV, vP, vVar;
204-
vector<const float *> mG, vG;
205-
vector<int64_t> mB, mR, mC, vN;
206-
vector<double> m_omb2, m_rho, v_omb2, v_rho;
179+
float f_wd_factor = 1.0 - (lr * weight_decay);
180+
bool zero_weight_decay = weight_decay == 0.0;
181+
vector<float> colsum;
182+
vector<float> inv_rvm;
183+
vector<float> update;
207184

208185
for (size_t i = 0; i < params.size(); ++i) {
186+
Tensor &p = params[i];
209187
state_steps[i].add_(1);
188+
if (p.numel() == 0) {
189+
continue; // A zero-sized parameter is a no-op.
190+
}
210191
double s = state_steps[i].item<double>();
211192
double omb2 = pow(s, beta2_decay);
212193
double rho = min(lr, 1.0 / sqrt(s));
213-
Tensor p = params[i];
214194
if (p.dim() > 1) {
215195
int64_t nd = p.dim();
216196
int64_t Cc = p.size(nd - 1);
217197
int64_t Rr = p.size(nd - 2);
218198
int64_t rc = Rr * Cc;
219-
// A zero-sized last dim makes rc == 0; guard the integer division.
220-
mB.push_back(rc == 0 ? 0 : p.numel() / rc);
221-
mR.push_back(Rr);
222-
mC.push_back(Cc);
223-
mP.push_back(params[i].mutable_data_ptr<float>());
224-
mG.push_back(grads[i].const_data_ptr<float>());
225-
mRV.push_back(row_vars[i].mutable_data_ptr<float>());
226-
mCV.push_back(col_vars[i].mutable_data_ptr<float>());
227-
m_omb2.push_back(omb2);
228-
m_rho.push_back(rho);
199+
int64_t B = p.numel() / rc;
200+
colsum.assign(B * Cc, 0.0f);
201+
inv_rvm.reserve(B);
202+
update.reserve(p.numel());
203+
adafactor_matrix(
204+
maximize, zero_weight_decay, p.mutable_data_ptr<float>(),
205+
grads[i].const_data_ptr<float>(), row_vars[i].mutable_data_ptr<float>(),
206+
col_vars[i].mutable_data_ptr<float>(), colsum.data(), inv_rvm.data(),
207+
update.data(), B, Rr, Cc, omb2, rho, eps1, eps2, d, f_wd_factor);
229208
} else {
230-
vN.push_back(p.numel());
231-
vP.push_back(params[i].mutable_data_ptr<float>());
232-
vG.push_back(grads[i].const_data_ptr<float>());
233-
vVar.push_back(variances[i].mutable_data_ptr<float>());
234-
v_omb2.push_back(omb2);
235-
v_rho.push_back(rho);
209+
update.reserve(p.numel());
210+
adafactor_vector(maximize, zero_weight_decay, p.mutable_data_ptr<float>(),
211+
grads[i].const_data_ptr<float>(),
212+
variances[i].mutable_data_ptr<float>(), update.data(),
213+
p.numel(), omb2, rho, eps1, eps2, d, f_wd_factor);
236214
}
237215
}
238-
239-
float f_eps1 = eps1;
240-
float f_eps1sq = (float)eps1 * (float)eps1;
241-
float f_eps2 = eps2;
242-
float f_d = d;
243-
float f_wd = 1.0f - (float)(lr * weight_decay);
244-
bool zero_weight_decay = weight_decay == 0.0;
245-
246-
adafactor_impl(maximize, zero_weight_decay, mP, mG, mRV, mCV, mB, mR, mC, m_omb2,
247-
m_rho, vP, vG, vVar, vN, v_omb2, v_rho, f_eps1, f_eps1sq, f_eps2,
248-
f_d, f_wd);
249216
}
250217

251218
#if PYBIND11_VERSION_HEX >= 0x020D0000 // pybind11 >= 2.13

roundpipe/optim/csrc/adagrad.cpp

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,25 @@ void adagrad_kernel(bool current_bool, Args... args) {
3131
void adagrad(vector<Tensor> params, vector<Tensor> grads, vector<Tensor> state_sum,
3232
vector<Tensor> state_steps, double lr, double lr_decay, double eps,
3333
double weight_decay, bool maximize) {
34-
vector<int64_t> numel(params.size());
35-
vector<float *> params_ptr(params.size());
36-
vector<const float *> grads_ptr(params.size());
37-
vector<float *> state_sum_ptr(params.size());
3834
for (size_t i = 0; i < params.size(); ++i) {
39-
numel[i] = params[i].numel();
40-
params_ptr[i] = params[i].mutable_data_ptr<float>();
41-
grads_ptr[i] = grads[i].const_data_ptr<float>();
42-
state_sum_ptr[i] = state_sum[i].mutable_data_ptr<float>();
4335
state_steps[i].add_(1);
4436
}
4537
#pragma omp parallel
4638
{
4739
int rank = omp_get_thread_num();
4840
int nthreads = omp_get_num_threads();
4941
for (size_t i = 0; i < params.size(); ++i) {
50-
int64_t block_size = numel[i] / nthreads + (rank < (numel[i] % nthreads));
42+
int64_t numel = params[i].numel();
43+
int64_t block_size = numel / nthreads + (rank < (numel % nthreads));
5144
int64_t offset =
52-
(numel[i] / nthreads) * rank + min<int64_t>(rank, numel[i] % nthreads);
45+
(numel / nthreads) * rank + min<int64_t>(rank, numel % nthreads);
5346
double step = state_steps[i].item<double>();
5447
double clr = lr / (1.0 + (step - 1.0) * lr_decay);
55-
adagrad_kernel(maximize, weight_decay == 0.0, params_ptr[i] + offset,
56-
grads_ptr[i] + offset, state_sum_ptr[i] + offset, clr, eps,
57-
weight_decay, block_size);
48+
float *params_ptr = params[i].mutable_data_ptr<float>() + offset;
49+
const float *grads_ptr = grads[i].const_data_ptr<float>() + offset;
50+
float *state_sum_ptr = state_sum[i].mutable_data_ptr<float>() + offset;
51+
adagrad_kernel(maximize, weight_decay == 0.0, params_ptr, grads_ptr,
52+
state_sum_ptr, clr, eps, weight_decay, block_size);
5853
}
5954
}
6055
}

0 commit comments

Comments
 (0)