@@ -17,41 +17,38 @@ using namespace torch;
1717template <bool maximize, bool zero_weight_decay>
1818static 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.
118116template <bool maximize, bool zero_weight_decay>
119117static 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.
184160template <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
0 commit comments