Skip to content

Commit 17c17e4

Browse files
authored
Reorder channelwise gated delta rule chunked hot loops for autovectorization (#21021) (#21021)
Summary: Reorder the chunked prefill inner loops (steps 1, 4, 5, 6) so the innermost loop runs contiguously over the head dimension (k or v) instead of striding down a column of the state / pv. This lets the compiler autovectorize the now-unit-stride AXPYs; hand-written at::vec was tried and was slower than the compiler output, so the loops stay scalar. Reviewed By: billmguo Differential Revision: D112598714
1 parent 96e87e3 commit 17c17e4

1 file changed

Lines changed: 66 additions & 38 deletions

File tree

extension/llm/custom_ops/op_sdpa.cpp

Lines changed: 66 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,14 +1013,22 @@ void channelwise_gated_delta_rule_chunked(
10131013
const auto* __restrict__ beta_c = beta_head + base;
10141014
auto* __restrict__ o_c = o_head + base * v_head_dim;
10151015

1016-
// 1. gc = cumsum_r(log decay) (per channel; resets each chunk)
1017-
for (const auto k_idx : c10::irange(k_head_dim)) {
1018-
float acc = 0.0f;
1019-
for (const auto r : c10::irange(cur)) {
1020-
acc += std::log(d_c[r * k_head_dim + k_idx]);
1021-
sc.gc[r * k_head_dim + k_idx] = acc;
1022-
sc.eg[r * k_head_dim + k_idx] = std::exp(acc);
1023-
sc.eg_inv[r * k_head_dim + k_idx] = std::exp(-acc);
1016+
// 1. gc = cumsum_r(log decay) (per channel; resets each chunk).
1017+
// Reordered so the inner loop runs contiguously over k; the running
1018+
// cumsum is read back from the previous gc row.
1019+
for (const auto r : c10::irange(cur)) {
1020+
const float* __restrict__ d_row = d_c + r * k_head_dim;
1021+
const float* __restrict__ gc_prev =
1022+
r == 0 ? nullptr : sc.gc + (r - 1) * k_head_dim;
1023+
float* __restrict__ gc_row = sc.gc + r * k_head_dim;
1024+
float* __restrict__ eg_row = sc.eg + r * k_head_dim;
1025+
float* __restrict__ eg_inv_row = sc.eg_inv + r * k_head_dim;
1026+
for (const auto k_idx : c10::irange(k_head_dim)) {
1027+
const float prev = gc_prev == nullptr ? 0.0f : gc_prev[k_idx];
1028+
const float acc = prev + std::log(d_row[k_idx]);
1029+
gc_row[k_idx] = acc;
1030+
eg_row[k_idx] = std::exp(acc);
1031+
eg_inv_row[k_idx] = std::exp(-acc);
10241032
}
10251033
}
10261034

@@ -1073,47 +1081,64 @@ void channelwise_gated_delta_rule_chunked(
10731081
}
10741082

10751083
// 4. WY packing: w = A @ (exp(gc) * k), u = A @ v (A lower-tri: j
1076-
// <= r)
1084+
// <= r). Reordered so the inner loop runs contiguously over k / v.
10771085
for (const auto r : c10::irange(cur)) {
1086+
float* __restrict__ w_row = sc.w + r * k_head_dim;
1087+
float* __restrict__ u_row = sc.u + r * v_head_dim;
10781088
for (const auto k_idx : c10::irange(k_head_dim)) {
1079-
float acc = 0.0f;
1080-
for (const auto j : c10::irange(r + 1)) {
1081-
acc += sc.A[r * CHUNK_SIZE + j] *
1082-
sc.eg[j * k_head_dim + k_idx] *
1083-
k_c[j * k_head_dim + k_idx];
1084-
}
1085-
sc.w[r * k_head_dim + k_idx] = acc;
1089+
w_row[k_idx] = 0.0f;
10861090
}
10871091
for (const auto v_idx : c10::irange(v_head_dim)) {
1088-
float acc = 0.0f;
1089-
for (const auto j : c10::irange(r + 1)) {
1090-
acc += sc.A[r * CHUNK_SIZE + j] * v_c[j * v_head_dim + v_idx];
1092+
u_row[v_idx] = 0.0f;
1093+
}
1094+
for (const auto j : c10::irange(r + 1)) {
1095+
const float arj = sc.A[r * CHUNK_SIZE + j];
1096+
const float* __restrict__ eg_row = sc.eg + j * k_head_dim;
1097+
const float* __restrict__ k_row = k_c + j * k_head_dim;
1098+
for (const auto k_idx : c10::irange(k_head_dim)) {
1099+
w_row[k_idx] += arj * eg_row[k_idx] * k_row[k_idx];
1100+
}
1101+
const float* __restrict__ v_row = v_c + j * v_head_dim;
1102+
for (const auto v_idx : c10::irange(v_head_dim)) {
1103+
u_row[v_idx] += arj * v_row[v_idx];
10911104
}
1092-
sc.u[r * v_head_dim + v_idx] = acc;
10931105
}
10941106
}
10951107

1096-
// 5. pv = u - w @ S_old ; o = (q ⊙ exp gc) @ S_old + Aqk @ pv
1108+
// 5. pv = u - w @ S_old ; o = (q ⊙ exp gc) @ S_old. Reordered so
1109+
// the inner loop runs contiguously over v; pv/o_c accumulate the
1110+
// k-sum, then pv is finalized as u - (w@S) with a single
1111+
// subtraction (bit-identical to the k-then-subtract form).
10971112
for (const auto r : c10::irange(cur)) {
1113+
float* __restrict__ pv_row = sc.pv + r * v_head_dim;
1114+
float* __restrict__ o_row = o_c + r * v_head_dim;
10981115
for (const auto v_idx : c10::irange(v_head_dim)) {
1099-
float wS = 0.0f, qS = 0.0f;
1100-
for (const auto k_idx : c10::irange(k_head_dim)) {
1101-
const float s = S[k_idx * v_head_dim + v_idx];
1102-
wS += sc.w[r * k_head_dim + k_idx] * s;
1103-
qS += q_c[r * k_head_dim + k_idx] *
1104-
sc.eg[r * k_head_dim + k_idx] * s;
1116+
pv_row[v_idx] = 0.0f;
1117+
o_row[v_idx] = 0.0f;
1118+
}
1119+
for (const auto k_idx : c10::irange(k_head_dim)) {
1120+
const float wrk = sc.w[r * k_head_dim + k_idx];
1121+
const float qrk =
1122+
q_c[r * k_head_dim + k_idx] * sc.eg[r * k_head_dim + k_idx];
1123+
const float* __restrict__ s_row = S + k_idx * v_head_dim;
1124+
for (const auto v_idx : c10::irange(v_head_dim)) {
1125+
pv_row[v_idx] += wrk * s_row[v_idx];
1126+
o_row[v_idx] += qrk * s_row[v_idx];
11051127
}
1106-
sc.pv[r * v_head_dim + v_idx] =
1107-
sc.u[r * v_head_dim + v_idx] - wS;
1108-
o_c[r * v_head_dim + v_idx] = qS;
1128+
}
1129+
const float* __restrict__ u_row = sc.u + r * v_head_dim;
1130+
for (const auto v_idx : c10::irange(v_head_dim)) {
1131+
pv_row[v_idx] = u_row[v_idx] - pv_row[v_idx];
11091132
}
11101133
}
1134+
// 5b. o += Aqk @ pv (AXPY over the contiguous v).
11111135
for (const auto r : c10::irange(cur)) {
1136+
float* __restrict__ o_row = o_c + r * v_head_dim;
11121137
for (const auto j : c10::irange(r + 1)) {
11131138
const float a = sc.Aqk[r * CHUNK_SIZE + j];
1139+
const float* __restrict__ pv_row = sc.pv + j * v_head_dim;
11141140
for (const auto v_idx : c10::irange(v_head_dim)) {
1115-
o_c[r * v_head_dim + v_idx] +=
1116-
a * sc.pv[j * v_head_dim + v_idx];
1141+
o_row[v_idx] += a * pv_row[v_idx];
11171142
}
11181143
}
11191144
}
@@ -1129,13 +1154,16 @@ void channelwise_gated_delta_rule_chunked(
11291154
for (const auto r : c10::irange(cur)) {
11301155
sc.de[r] = std::exp(gc_last - sc.gc[r * k_head_dim + k_idx]);
11311156
}
1157+
float* __restrict__ S_row = S + k_idx * v_head_dim;
11321158
for (const auto v_idx : c10::irange(v_head_dim)) {
1133-
float acc = S[k_idx * v_head_dim + v_idx] * eg_last;
1134-
for (const auto r : c10::irange(cur)) {
1135-
acc += k_c[r * k_head_dim + k_idx] * sc.de[r] *
1136-
sc.pv[r * v_head_dim + v_idx];
1159+
S_row[v_idx] *= eg_last;
1160+
}
1161+
for (const auto r : c10::irange(cur)) {
1162+
const float c = k_c[r * k_head_dim + k_idx] * sc.de[r];
1163+
const float* __restrict__ pv_row = sc.pv + r * v_head_dim;
1164+
for (const auto v_idx : c10::irange(v_head_dim)) {
1165+
S_row[v_idx] += c * pv_row[v_idx];
11371166
}
1138-
S[k_idx * v_head_dim + v_idx] = acc;
11391167
}
11401168
}
11411169
} // chunk
@@ -1311,4 +1339,4 @@ const auto channelwise_gated_delta_rule_out_registration =
13111339
"llama::channelwise_gated_delta_rule.out",
13121340
channelwise_gated_delta_rule_out_boxed));
13131341

1314-
} // namespace
1342+
} // namespace

0 commit comments

Comments
 (0)