src/cpu/packed_encoder.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ─── CPU packed-encoder ops ───────────────────────────────────────────────── | ||
| 2 | // | ||
| 3 | // FP32 reference implementations of the ops a packed (variable-length, | ||
| 4 | // unpadded) encoder batch needs — the parity oracle for the CUDA kernels in | ||
| 5 | // src/cuda/flash_attention_packed.cu and src/cuda/packed_encoder.cu: | ||
| 6 | // | ||
| 7 | // flash_attention_packed_qkv_forward — bidirectional (optionally windowed) | ||
| 8 | // self-attention off a fused (L, 3*H*hd) QKV, per-row sequence bounds. | ||
| 9 | // rope_qkv_packed_inplace — RoPE on the Q/K sections, per-row pos. | ||
| 10 | // segment_softmax_stats — [top1, top1-top2, norm. entropy, k/255] | ||
| 11 | // per variable-length logit segment. | ||
| 12 | |||
| 13 | #include <brotensor/tensor.h> | ||
| 14 | |||
| 15 | #include <algorithm> | ||
| 16 | #include <cmath> | ||
| 17 | #include <cstdint> | ||
| 18 | #include <stdexcept> | ||
| 19 | #include <string> | ||
| 20 | #include <vector> | ||
| 21 | |||
| 22 | namespace brotensor::detail::cpu { | ||
| 23 | |||
| 24 | namespace { | ||
| 25 | |||
| 26 | ✗ | [[noreturn]] void fail(const char* op, const std::string& reason) { | |
| 27 | ✗ | throw std::runtime_error(std::string("brotensor: ") + op + ": " + reason); | |
| 28 | ✗ | } | |
| 29 | |||
| 30 | 1806 | void need_fp32(const ::brotensor::Tensor& t, const char* op, const char* name) { | |
| 31 |
1/6✓ Branch 0 taken 1806 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
1806 | if (t.dtype != Dtype::FP32) fail(op, std::string(name) + " must be FP32 (CPU backend is FP32-only)"); |
| 32 | 1806 | } | |
| 33 | |||
| 34 | } // namespace | ||
| 35 | |||
| 36 | 1192 | void flash_attention_packed_qkv_forward(const ::brotensor::Tensor& QKV, | |
| 37 | const ::brotensor::Tensor& seq_bounds, | ||
| 38 | int num_heads, int window, | ||
| 39 | ::brotensor::Tensor& O) { | ||
| 40 | 1192 | constexpr const char* op = "flash_attention_packed_qkv_forward"; | |
| 41 | 1192 | need_fp32(QKV, op, "QKV"); | |
| 42 |
1/4✓ Branch 0 taken 1192 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1192 | if (seq_bounds.dtype != Dtype::INT32) fail(op, "seq_bounds must be INT32"); |
| 43 |
1/2✓ Branch 0 taken 1192 times.
✗ Branch 1 not taken.
|
1192 | if (num_heads <= 0 || QKV.cols % (3 * num_heads) != 0) { |
| 44 | ✗ | fail(op, "QKV.cols must be 3 * num_heads * head_dim"); | |
| 45 | } | ||
| 46 | 1192 | const int L = QKV.rows; | |
| 47 |
1/4✓ Branch 0 taken 1192 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1192 | if (seq_bounds.rows != L || seq_bounds.cols != 2) fail(op, "seq_bounds must be (L, 2)"); |
| 48 | 1192 | const int D = QKV.cols / 3; | |
| 49 | 1192 | const int hd = D / num_heads; | |
| 50 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 1192 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
1192 | if (O.rows != L || O.cols != D || O.dtype != Dtype::FP32) O.resize(L, D, Dtype::FP32); |
| 51 |
1/2✓ Branch 0 taken 1192 times.
✗ Branch 1 not taken.
|
1192 | if (L == 0) return; |
| 52 | |||
| 53 | 1192 | const float* qkv = QKV.host_f32(); | |
| 54 | 1192 | const int32_t* b = static_cast<const int32_t*>(seq_bounds.host_raw()); | |
| 55 | 1192 | float* out = O.host_f32_mut(); | |
| 56 | 1192 | const float scale = 1.0f / std::sqrt(static_cast<float>(hd)); | |
| 57 |
2/2✓ Branch 0 taken 679 times.
✓ Branch 1 taken 513 times.
|
1192 | const int hw = window > 0 ? window / 2 : -1; |
| 58 | 1192 | std::vector<float> s; | |
| 59 |
2/2✓ Branch 0 taken 57242 times.
✓ Branch 1 taken 1192 times.
|
58434 | for (int r = 0; r < L; ++r) { |
| 60 | 57242 | const int start = b[2 * r], end = b[2 * r + 1]; | |
| 61 |
0/4✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
57242 | if (start < 0 || end > L || r < start || r >= end) fail(op, "row outside its seq_bounds"); |
| 62 |
3/4✓ Branch 0 taken 36045 times.
✓ Branch 1 taken 21197 times.
✓ Branch 2 taken 36045 times.
✗ Branch 3 not taken.
|
57242 | const int lo = hw >= 0 ? std::max(start, r - hw) : start; |
| 63 |
3/4✓ Branch 0 taken 36045 times.
✓ Branch 1 taken 21197 times.
✓ Branch 2 taken 36045 times.
✗ Branch 3 not taken.
|
57242 | const int hi = hw >= 0 ? std::min(end, r + hw + 1) : end; |
| 64 |
1/2✓ Branch 0 taken 57242 times.
✗ Branch 1 not taken.
|
57242 | s.assign(static_cast<std::size_t>(hi - lo), 0.0f); |
| 65 |
2/2✓ Branch 0 taken 118342 times.
✓ Branch 1 taken 57242 times.
|
175584 | for (int h = 0; h < num_heads; ++h) { |
| 66 | 118342 | const float* q = qkv + static_cast<std::size_t>(r) * 3 * D + h * hd; | |
| 67 | 118342 | float mx = -INFINITY; | |
| 68 |
2/2✓ Branch 0 taken 118342 times.
✓ Branch 1 taken 3517272 times.
|
3635614 | for (int j = lo; j < hi; ++j) { |
| 69 | 3517272 | const float* k = qkv + static_cast<std::size_t>(j) * 3 * D + D + h * hd; | |
| 70 | 3517272 | float dot = 0.0f; | |
| 71 |
2/2✓ Branch 0 taken 161536824 times.
✓ Branch 1 taken 3517272 times.
|
165054096 | for (int d = 0; d < hd; ++d) dot += q[d] * k[d]; |
| 72 | 3517272 | s[static_cast<std::size_t>(j - lo)] = dot * scale; | |
| 73 |
1/2✓ Branch 0 taken 3517272 times.
✗ Branch 1 not taken.
|
3517272 | mx = std::max(mx, dot * scale); |
| 74 | 3517272 | } | |
| 75 | 118342 | float sum = 0.0f; | |
| 76 |
2/2✓ Branch 0 taken 3517272 times.
✓ Branch 1 taken 118342 times.
|
3635614 | for (float& v : s) { |
| 77 | 3517272 | v = std::exp(v - mx); | |
| 78 | 3517272 | sum += v; | |
| 79 | } | ||
| 80 | 118342 | float* o = out + static_cast<std::size_t>(r) * D + h * hd; | |
| 81 |
2/2✓ Branch 0 taken 1631032 times.
✓ Branch 1 taken 118342 times.
|
1749374 | for (int d = 0; d < hd; ++d) o[d] = 0.0f; |
| 82 |
2/2✓ Branch 0 taken 3517272 times.
✓ Branch 1 taken 118342 times.
|
3635614 | for (int j = lo; j < hi; ++j) { |
| 83 | 3517272 | const float* v = qkv + static_cast<std::size_t>(j) * 3 * D + 2 * D + h * hd; | |
| 84 | 3517272 | const float p = s[static_cast<std::size_t>(j - lo)] / sum; | |
| 85 |
2/2✓ Branch 0 taken 161536824 times.
✓ Branch 1 taken 3517272 times.
|
165054096 | for (int d = 0; d < hd; ++d) o[d] += p * v[d]; |
| 86 | 3517272 | } | |
| 87 | 118342 | } | |
| 88 | 57242 | } | |
| 89 | 1192 | } | |
| 90 | |||
| 91 | 8 | void flash_attention_packed_qkv_backward(const ::brotensor::Tensor& QKV, const ::brotensor::Tensor& dO, | |
| 92 | const ::brotensor::Tensor& seq_bounds, int num_heads, int window, | ||
| 93 | ::brotensor::Tensor& dQKV) { | ||
| 94 | 8 | constexpr const char* op = "flash_attention_packed_qkv_backward"; | |
| 95 | 8 | need_fp32(QKV, op, "QKV"); | |
| 96 | 8 | need_fp32(dO, op, "dO"); | |
| 97 |
1/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
8 | if (seq_bounds.dtype != Dtype::INT32) fail(op, "seq_bounds must be INT32"); |
| 98 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | if (num_heads <= 0 || QKV.cols % (3 * num_heads) != 0) { |
| 99 | ✗ | fail(op, "QKV.cols must be 3 * num_heads * head_dim"); | |
| 100 | } | ||
| 101 | 8 | const int L = QKV.rows; | |
| 102 | 8 | const int D = QKV.cols / 3; | |
| 103 | 8 | const int hd = D / num_heads; | |
| 104 |
1/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
8 | if (seq_bounds.rows != L || seq_bounds.cols != 2) fail(op, "seq_bounds must be (L, 2)"); |
| 105 |
1/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
8 | if (dO.rows != L || dO.cols != D) fail(op, "dO must be (L, num_heads*head_dim)"); |
| 106 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
8 | if (dQKV.rows != L || dQKV.cols != 3 * D || dQKV.dtype != Dtype::FP32) dQKV.resize(L, 3 * D, Dtype::FP32); |
| 107 | 8 | dQKV.zero(); | |
| 108 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | if (L == 0) return; |
| 109 | |||
| 110 | 8 | const float* qkv = QKV.host_f32(); | |
| 111 | 8 | const float* go = dO.host_f32(); | |
| 112 | 8 | const int32_t* b = static_cast<const int32_t*>(seq_bounds.host_raw()); | |
| 113 | 8 | float* g = dQKV.host_f32_mut(); | |
| 114 | 8 | const float scale = 1.0f / std::sqrt(static_cast<float>(hd)); | |
| 115 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | const int hw = window > 0 ? window / 2 : -1; |
| 116 | 8 | std::vector<float> p; | |
| 117 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | std::vector<float> o(static_cast<std::size_t>(hd)); |
| 118 |
2/2✓ Branch 0 taken 2272 times.
✓ Branch 1 taken 8 times.
|
2280 | for (int r = 0; r < L; ++r) { |
| 119 | 2272 | const int start = b[2 * r], end = b[2 * r + 1]; | |
| 120 |
0/4✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
2272 | if (start < 0 || end > L || r < start || r >= end) fail(op, "row outside its seq_bounds"); |
| 121 |
3/4✓ Branch 0 taken 1435 times.
✓ Branch 1 taken 837 times.
✓ Branch 2 taken 1435 times.
✗ Branch 3 not taken.
|
2272 | const int lo = hw >= 0 ? std::max(start, r - hw) : start; |
| 122 |
3/4✓ Branch 0 taken 1435 times.
✓ Branch 1 taken 837 times.
✓ Branch 2 taken 1435 times.
✗ Branch 3 not taken.
|
2272 | const int hi = hw >= 0 ? std::min(end, r + hw + 1) : end; |
| 123 |
1/2✓ Branch 0 taken 2272 times.
✗ Branch 1 not taken.
|
2272 | p.assign(static_cast<std::size_t>(hi - lo), 0.0f); |
| 124 |
2/2✓ Branch 0 taken 7208 times.
✓ Branch 1 taken 2272 times.
|
9480 | for (int h = 0; h < num_heads; ++h) { |
| 125 | 7208 | const float* q = qkv + static_cast<std::size_t>(r) * 3 * D + h * hd; | |
| 126 | 7208 | const float* dout = go + static_cast<std::size_t>(r) * D + h * hd; | |
| 127 | 7208 | float mx = -INFINITY; | |
| 128 |
2/2✓ Branch 0 taken 7208 times.
✓ Branch 1 taken 579592 times.
|
586800 | for (int j = lo; j < hi; ++j) { |
| 129 | 579592 | const float* k = qkv + static_cast<std::size_t>(j) * 3 * D + D + h * hd; | |
| 130 | 579592 | float dot = 0.0f; | |
| 131 |
2/2✓ Branch 0 taken 36700256 times.
✓ Branch 1 taken 579592 times.
|
37279848 | for (int d = 0; d < hd; ++d) dot += q[d] * k[d]; |
| 132 | 579592 | p[static_cast<std::size_t>(j - lo)] = dot * scale; | |
| 133 |
1/2✓ Branch 0 taken 579592 times.
✗ Branch 1 not taken.
|
579592 | mx = std::max(mx, dot * scale); |
| 134 | 579592 | } | |
| 135 | 7208 | float sum = 0.0f; | |
| 136 |
2/2✓ Branch 0 taken 579592 times.
✓ Branch 1 taken 7208 times.
|
586800 | for (float& v : p) { |
| 137 | 579592 | v = std::exp(v - mx); | |
| 138 | 579592 | sum += v; | |
| 139 | } | ||
| 140 |
1/2✓ Branch 0 taken 7208 times.
✗ Branch 1 not taken.
|
7208 | std::fill(o.begin(), o.end(), 0.0f); |
| 141 |
2/2✓ Branch 0 taken 579592 times.
✓ Branch 1 taken 7208 times.
|
586800 | for (int j = lo; j < hi; ++j) { |
| 142 | 579592 | float& pj = p[static_cast<std::size_t>(j - lo)]; | |
| 143 | 579592 | pj /= sum; | |
| 144 | 579592 | const float* v = qkv + static_cast<std::size_t>(j) * 3 * D + 2 * D + h * hd; | |
| 145 |
2/2✓ Branch 0 taken 36700256 times.
✓ Branch 1 taken 579592 times.
|
37279848 | for (int d = 0; d < hd; ++d) o[static_cast<std::size_t>(d)] += pj * v[d]; |
| 146 | 579592 | } | |
| 147 | 7208 | float drow = 0.0f; // D_r = dO_r . O_r = sum_j P_rj dP_rj | |
| 148 |
2/2✓ Branch 0 taken 396384 times.
✓ Branch 1 taken 7208 times.
|
403592 | for (int d = 0; d < hd; ++d) drow += dout[d] * o[static_cast<std::size_t>(d)]; |
| 149 | 7208 | float* dq = g + static_cast<std::size_t>(r) * 3 * D + h * hd; | |
| 150 |
2/2✓ Branch 0 taken 579592 times.
✓ Branch 1 taken 7208 times.
|
586800 | for (int j = lo; j < hi; ++j) { |
| 151 | 579592 | const float pj = p[static_cast<std::size_t>(j - lo)]; | |
| 152 | 579592 | const float* k = qkv + static_cast<std::size_t>(j) * 3 * D + D + h * hd; | |
| 153 | 579592 | const float* v = qkv + static_cast<std::size_t>(j) * 3 * D + 2 * D + h * hd; | |
| 154 | 579592 | float* dk = g + static_cast<std::size_t>(j) * 3 * D + D + h * hd; | |
| 155 | 579592 | float* dv = g + static_cast<std::size_t>(j) * 3 * D + 2 * D + h * hd; | |
| 156 | 579592 | float dp = 0.0f; | |
| 157 |
2/2✓ Branch 0 taken 36700256 times.
✓ Branch 1 taken 579592 times.
|
37279848 | for (int d = 0; d < hd; ++d) dp += dout[d] * v[d]; |
| 158 | 579592 | const float ds = pj * (dp - drow) * scale; | |
| 159 |
2/2✓ Branch 0 taken 36700256 times.
✓ Branch 1 taken 579592 times.
|
37279848 | for (int d = 0; d < hd; ++d) { |
| 160 | 36700256 | dv[d] += pj * dout[d]; | |
| 161 | 36700256 | dq[d] += ds * k[d]; | |
| 162 | 36700256 | dk[d] += ds * q[d]; | |
| 163 | 36700256 | } | |
| 164 | 579592 | } | |
| 165 | 7208 | } | |
| 166 | 2272 | } | |
| 167 | 8 | } | |
| 168 | |||
| 169 | 2 | void rope_qkv_packed_inplace(::brotensor::Tensor& QKV, const ::brotensor::Tensor& cos_tbl, | |
| 170 | const ::brotensor::Tensor& sin_tbl, const ::brotensor::Tensor& pos, | ||
| 171 | int num_heads, int head_dim) { | ||
| 172 | 2 | constexpr const char* op = "rope_qkv_packed_inplace"; | |
| 173 | 2 | need_fp32(QKV, op, "QKV"); | |
| 174 | 2 | need_fp32(cos_tbl, op, "cos_tbl"); | |
| 175 | 2 | need_fp32(sin_tbl, op, "sin_tbl"); | |
| 176 |
1/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
2 | if (pos.dtype != Dtype::INT32 || pos.rows != QKV.rows) fail(op, "pos must be (L, 1) INT32"); |
| 177 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (head_dim <= 0 || (head_dim & 1) || num_heads <= 0 || QKV.cols != 3 * num_heads * head_dim) { |
| 178 | ✗ | fail(op, "QKV.cols must be 3 * num_heads * head_dim with head_dim even"); | |
| 179 | } | ||
| 180 | 2 | const int half = head_dim / 2; | |
| 181 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (cos_tbl.cols != half || sin_tbl.cols != half || sin_tbl.rows != cos_tbl.rows) { |
| 182 | ✗ | fail(op, "cos_tbl / sin_tbl must be (P, head_dim/2)"); | |
| 183 | } | ||
| 184 | 2 | const int L = QKV.rows; | |
| 185 | 2 | const int D = num_heads * head_dim; | |
| 186 | 2 | float* x = QKV.host_f32_mut(); | |
| 187 | 2 | const float* ct = cos_tbl.host_f32(); | |
| 188 | 2 | const float* st = sin_tbl.host_f32(); | |
| 189 | 2 | const int32_t* p = static_cast<const int32_t*>(pos.host_raw()); | |
| 190 |
2/2✓ Branch 0 taken 81 times.
✓ Branch 1 taken 2 times.
|
83 | for (int r = 0; r < L; ++r) { |
| 191 | 81 | const int pr = p[r]; | |
| 192 |
1/4✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
81 | if (pr < 0 || pr >= cos_tbl.rows) fail(op, "pos outside the cos/sin table"); |
| 193 | 81 | float* row = x + static_cast<std::size_t>(r) * 3 * D; | |
| 194 |
2/2✓ Branch 0 taken 162 times.
✓ Branch 1 taken 81 times.
|
243 | for (int sec = 0; sec < 2; ++sec) { |
| 195 |
2/2✓ Branch 0 taken 1638 times.
✓ Branch 1 taken 162 times.
|
1800 | for (int h = 0; h < num_heads; ++h) { |
| 196 | 1638 | float* v = row + sec * D + h * head_dim; | |
| 197 |
2/2✓ Branch 0 taken 49560 times.
✓ Branch 1 taken 1638 times.
|
51198 | for (int i = 0; i < half; ++i) { |
| 198 | 49560 | const float c = ct[static_cast<std::size_t>(pr) * half + i]; | |
| 199 | 49560 | const float s = st[static_cast<std::size_t>(pr) * half + i]; | |
| 200 | 49560 | const float x0 = v[2 * i], x1 = v[2 * i + 1]; | |
| 201 | 49560 | v[2 * i] = x0 * c - x1 * s; | |
| 202 | 49560 | v[2 * i + 1] = x0 * s + x1 * c; | |
| 203 | 49560 | } | |
| 204 | 1638 | } | |
| 205 | 162 | } | |
| 206 | 81 | } | |
| 207 | 2 | } | |
| 208 | |||
| 209 | 1 | void segment_softmax_stats(const ::brotensor::Tensor& logits, | |
| 210 | const ::brotensor::Tensor& seg_offsets, | ||
| 211 | ::brotensor::Tensor& out) { | ||
| 212 | 1 | constexpr const char* op = "segment_softmax_stats"; | |
| 213 | 1 | need_fp32(logits, op, "logits"); | |
| 214 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1 | if (seg_offsets.dtype != Dtype::INT32 || seg_offsets.rows < 1) { |
| 215 | ✗ | fail(op, "seg_offsets must be (S+1, 1) INT32"); | |
| 216 | } | ||
| 217 | 1 | const int S = seg_offsets.rows - 1; | |
| 218 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
1 | if (out.rows != S || out.cols != 4 || out.dtype != Dtype::FP32) out.resize(S, 4, Dtype::FP32); |
| 219 | 1 | const float* l = logits.host_f32(); | |
| 220 | 1 | const int32_t* off = static_cast<const int32_t*>(seg_offsets.host_raw()); | |
| 221 | 1 | float* o = out.host_f32_mut(); | |
| 222 | 1 | std::vector<float> p; | |
| 223 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 time.
|
5 | for (int s = 0; s < S; ++s) { |
| 224 | 4 | const int a = off[s], b = off[s + 1]; | |
| 225 | 4 | float* row = o + static_cast<std::size_t>(s) * 4; | |
| 226 |
1/6✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
4 | if (a < 0 || b < a || b > logits.size()) fail(op, "seg_offsets out of range"); |
| 227 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
|
4 | if (b == a) { |
| 228 | 1 | row[0] = row[1] = row[2] = row[3] = 0.0f; | |
| 229 | 1 | continue; | |
| 230 | } | ||
| 231 | 3 | const int n = b - a; | |
| 232 | 3 | float mx = -INFINITY; | |
| 233 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
|
17 | for (int i = a; i < b; ++i) mx = std::max(mx, l[i]); |
| 234 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | p.assign(static_cast<std::size_t>(n), 0.0f); |
| 235 | 3 | float sum = 0.0f; | |
| 236 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 3 times.
|
17 | for (int i = 0; i < n; ++i) { |
| 237 | 14 | p[static_cast<std::size_t>(i)] = std::exp(l[a + i] - mx); | |
| 238 | 14 | sum += p[static_cast<std::size_t>(i)]; | |
| 239 | 14 | } | |
| 240 | 3 | float top1 = 0.0f, top2 = 0.0f, ent = 0.0f; | |
| 241 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 14 times.
|
17 | for (float& v : p) { |
| 242 | 14 | v /= sum; | |
| 243 |
1/2✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
|
14 | ent -= v * std::log(std::max(v, 1e-9f)); |
| 244 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10 times.
|
14 | if (v > top1) { |
| 245 | 4 | top2 = top1; | |
| 246 | 4 | top1 = v; | |
| 247 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
|
14 | } else if (v > top2) { |
| 248 | 2 | top2 = v; | |
| 249 | 2 | } | |
| 250 | } | ||
| 251 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | const float k = std::max(2.0f, static_cast<float>(n)); |
| 252 | 3 | row[0] = top1; | |
| 253 | 3 | row[1] = top1 - top2; | |
| 254 | 3 | row[2] = ent / std::log(k); | |
| 255 | 3 | row[3] = k / 255.0f; | |
| 256 | 3 | } | |
| 257 | 1 | } | |
| 258 | |||
| 259 | namespace { | ||
| 260 | |||
| 261 | 22054806 | float apply_act(float v, int act) { | |
| 262 |
5/6✓ Branch 0 taken 293103 times.
✓ Branch 1 taken 6893772 times.
✓ Branch 2 taken 1165087 times.
✓ Branch 3 taken 13686460 times.
✓ Branch 4 taken 16384 times.
✗ Branch 5 not taken.
|
22054806 | switch (act) { |
| 263 |
1/2✓ Branch 0 taken 16384 times.
✗ Branch 1 not taken.
|
16384 | case 1: return v > 0.0f ? v : 0.0f; |
| 264 | 293103 | case 2: return 0.5f * v * (1.0f + std::tanh(0.7978845608f * (v + 0.044715f * v * v * v))); | |
| 265 | 6893772 | case 3: return 0.5f * v * (1.0f + std::erf(v * 0.70710678118f)); | |
| 266 | 1165087 | case 4: return v / (1.0f + std::exp(-v)); | |
| 267 | ✗ | case 5: return v / (1.0f + std::exp(-1.702f * v)); | |
| 268 | 13686460 | default: return v; | |
| 269 | } | ||
| 270 | 22054806 | } | |
| 271 | |||
| 272 | } // namespace | ||
| 273 | |||
| 274 | 221 | void linear_forward_batched_ex(const ::brotensor::Tensor& W, const ::brotensor::Tensor* bias, | |
| 275 | const ::brotensor::Tensor& X, int act, int epilogue_flags, | ||
| 276 | ::brotensor::Tensor* /*workspace: CPU never splits*/, | ||
| 277 | ::brotensor::Tensor& Y) { | ||
| 278 | 221 | constexpr const char* op = "linear_forward_batched_ex"; | |
| 279 | 221 | const int epilogue = epilogue_flags & ~16; // kLinearEpiFastAccum: FP32 here regardless | |
| 280 | 221 | need_fp32(W, op, "W"); | |
| 281 | 221 | need_fp32(X, op, "X"); | |
| 282 |
2/2✓ Branch 0 taken 149 times.
✓ Branch 1 taken 72 times.
|
221 | if (bias) need_fp32(*bias, op, "bias"); |
| 283 | 221 | const int N = W.rows, K = W.cols, M = X.rows; | |
| 284 |
1/4✓ Branch 0 taken 221 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
221 | if (X.cols != K) fail(op, "X.cols must equal W.cols"); |
| 285 |
3/6✓ Branch 0 taken 149 times.
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 149 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
221 | if (bias && static_cast<long long>(bias->rows) * bias->cols != N) fail(op, "bias size must equal W.rows"); |
| 286 |
1/4✓ Branch 0 taken 221 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
221 | if (epilogue < 0 || epilogue > 3) fail(op, "unknown epilogue"); |
| 287 |
5/8✓ Branch 0 taken 186 times.
✓ Branch 1 taken 35 times.
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 153 times.
✓ Branch 4 taken 68 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
221 | if ((epilogue == 2 || epilogue == 3) && (act != 0 || N % 2 != 0)) fail(op, "geglu/swiglu needs act 0 and even W.rows"); |
| 288 |
4/4✓ Branch 0 taken 186 times.
✓ Branch 1 taken 35 times.
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 153 times.
|
221 | const int out_cols = (epilogue == 2 || epilogue == 3) ? N / 2 : N; |
| 289 |
2/2✓ Branch 0 taken 59 times.
✓ Branch 1 taken 162 times.
|
221 | if (epilogue == 1) { |
| 290 |
1/4✓ Branch 0 taken 59 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
59 | if (Y.rows != M || Y.cols != N || Y.dtype != Dtype::FP32) fail(op, "accumulate needs Y (B, out) FP32"); |
| 291 |
5/6✓ Branch 0 taken 160 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 147 times.
✓ Branch 3 taken 13 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 147 times.
|
221 | } else if (Y.rows != M || Y.cols != out_cols || Y.dtype != Dtype::FP32) { |
| 292 | 15 | Y.resize(M, out_cols, Dtype::FP32); | |
| 293 | 15 | } | |
| 294 |
1/2✓ Branch 0 taken 221 times.
✗ Branch 1 not taken.
|
221 | if (M == 0) return; |
| 295 | 221 | const float* w = W.host_f32(); | |
| 296 | 221 | const float* x = X.host_f32(); | |
| 297 |
2/2✓ Branch 0 taken 149 times.
✓ Branch 1 taken 72 times.
|
221 | const float* bb = bias ? bias->host_f32() : nullptr; |
| 298 | 221 | float* y = Y.host_f32_mut(); | |
| 299 | 221 | std::vector<float> r(static_cast<std::size_t>(N)); | |
| 300 |
2/2✓ Branch 0 taken 20322 times.
✓ Branch 1 taken 221 times.
|
20543 | for (int m = 0; m < M; ++m) { |
| 301 | 20322 | const float* xr = x + static_cast<std::size_t>(m) * K; | |
| 302 |
2/2✓ Branch 0 taken 19200857 times.
✓ Branch 1 taken 20322 times.
|
19221179 | for (int n = 0; n < N; ++n) { |
| 303 | 19200857 | const float* wr = w + static_cast<std::size_t>(n) * K; | |
| 304 | 19200857 | float acc = 0.0f; | |
| 305 |
2/2✓ Branch 0 taken 11832275629 times.
✓ Branch 1 taken 19200857 times.
|
11851476486 | for (int k = 0; k < K; ++k) acc += xr[k] * wr[k]; |
| 306 |
3/4✓ Branch 0 taken 9854819 times.
✓ Branch 1 taken 9346038 times.
✓ Branch 2 taken 19200857 times.
✗ Branch 3 not taken.
|
19200857 | r[static_cast<std::size_t>(n)] = apply_act(acc + (bb ? bb[n] : 0.0f), act); |
| 307 | 19200857 | } | |
| 308 | 20322 | float* yr = y + static_cast<std::size_t>(m) * out_cols; | |
| 309 |
2/2✓ Branch 0 taken 11350 times.
✓ Branch 1 taken 8972 times.
|
20322 | if (epilogue == 0) { |
| 310 |
2/2✓ Branch 0 taken 8124783 times.
✓ Branch 1 taken 8972 times.
|
8133755 | for (int n = 0; n < N; ++n) yr[n] = r[static_cast<std::size_t>(n)]; |
| 311 |
2/2✓ Branch 0 taken 5913 times.
✓ Branch 1 taken 5437 times.
|
20322 | } else if (epilogue == 1) { |
| 312 |
2/2✓ Branch 0 taken 5368176 times.
✓ Branch 1 taken 5913 times.
|
5374089 | for (int n = 0; n < N; ++n) yr[n] += r[static_cast<std::size_t>(n)]; |
| 313 |
2/2✓ Branch 0 taken 4724 times.
✓ Branch 1 taken 713 times.
|
11350 | } else if (epilogue == 2) { |
| 314 |
2/2✓ Branch 0 taken 4724 times.
✓ Branch 1 taken 2661749 times.
|
2666473 | for (int j = 0; j < out_cols; ++j) |
| 315 |
1/2✓ Branch 0 taken 2661749 times.
✗ Branch 1 not taken.
|
2661749 | yr[j] = r[static_cast<std::size_t>(2 * j)] * apply_act(r[static_cast<std::size_t>(2 * j + 1)], 3); |
| 316 | 4724 | } else { | |
| 317 |
2/2✓ Branch 0 taken 713 times.
✓ Branch 1 taken 192200 times.
|
192913 | for (int j = 0; j < out_cols; ++j) |
| 318 |
1/2✓ Branch 0 taken 192200 times.
✗ Branch 1 not taken.
|
192200 | yr[j] = apply_act(r[static_cast<std::size_t>(j)], 4) * r[static_cast<std::size_t>(out_cols + j)]; |
| 319 | } | ||
| 320 | 20322 | } | |
| 321 | 221 | } | |
| 322 | |||
| 323 | } // namespace brotensor::detail::cpu | ||
| 324 |