14int product(
const int *dims,
int rank) {
16 for (
int i = 0; i < rank; ++i) {
17 if (dims[i] <= 0)
throw Exception(
"Tensor kernel: dims must be > 0");
29 default:
throw Exception(
"Tensor kernel: not a binary op");
34void broadcastStrides(
const int *inDims,
int inRank,
int outRank,
int *strides) {
35 const int pad = outRank - inRank;
36 for (
int k = 0; k < outRank; ++k) {
37 const int d = k < pad ? 1 : inDims[k - pad];
39 for (
int t = k + 1; t < outRank; ++t) {
40 const int dt = t < pad ? 1 : inDims[t - pad];
43 strides[k] = (
d == 1) ? 0 :
s;
47int normalizeAxis(
int axis,
int rank) {
48 if (axis < 0) axis += rank;
49 if (axis < 0 || axis >= rank)
throw Exception(
"Tensor kernel: axis out of range");
69 return 0.5f *
x * (1.f + std::tanh(0.7978845608028654f * (
x + 0.044715f *
x *
x *
x)));
77 float lo = s0, hi = s1;
78 if (lo > hi) std::swap(lo, hi);
79 return std::clamp(
x, lo, hi);
120bool broadcastShape(
const int *aDims,
int aRank,
const int *bDims,
int bRank,
int *outDims,
122 const int r = std::max(aRank, bRank);
125 const int aPad = r - aRank;
126 const int bPad = r - bRank;
127 for (
int k = 0; k < r; ++k) {
128 const int da = k < aPad ? 1 : aDims[k - aPad];
129 const int db = k < bPad ? 1 : bDims[k - bPad];
130 if (da != db && da != 1 && db != 1)
return false;
131 outDims[k] = std::max(da, db);
137 const int *bDims,
int bRank,
float *out,
const int *outDims,
int outRank) {
140 broadcastStrides(aDims, aRank, outRank, aStrides);
141 broadcastStrides(bDims, bRank, outRank, bStrides);
145 for (
int k = outRank - 2; k >= 0; --k) S[k] = S[k + 1] * outDims[k + 1];
147 const int count = product(outDims, outRank);
148 for (
int o = 0; o < count; ++o) {
150 for (
int k = 0; k < outRank; ++k) {
151 const int coord = (o / S[k]) % outDims[k];
152 ia += coord * aStrides[k];
153 ib += coord * bStrides[k];
155 out[o] = applyBinary(
type,
a[ia],
b[ib]);
160 for (
int i = 0; i < count; ++i) out[i] =
applyUnary(
type, in[i], s0, s1);
163void softmax(
const float *in,
const int *dims,
int rank,
int axis,
bool logMode,
float *out) {
164 axis = normalizeAxis(axis, rank);
165 int outer = 1,
reduce = 1, inner = 1;
166 for (
int k = 0; k < axis; ++k) outer *= dims[k];
168 for (
int k = axis + 1; k < rank; ++k) inner *= dims[k];
169 const int rows = outer * inner;
170 for (
int r = 0; r < rows; ++r) {
171 const int o = r / inner;
172 const int i = r % inner;
173 const float *row = in + (o *
reduce) * inner + i;
174 float mx = -std::numeric_limits<float>::infinity();
175 for (
int j = 0; j <
reduce; ++j) mx = std::max(mx, row[j * inner]);
177 for (
int j = 0; j <
reduce; ++j) sum += std::exp(
double(row[j * inner] - mx));
178 const float invSum = float(1.0 / sum);
179 for (
int j = 0; j <
reduce; ++j) {
180 const float v = row[j * inner];
181 out[(o *
reduce + j) * inner + i] =
182 logMode ? (
v - mx) - std::log(
float(sum)) : std::exp(
v - mx) * invSum;
187void layernorm(
const float *in,
int rows,
int cols,
const float *
scale,
const float *bias,
188 float eps,
float *out) {
189 for (
int r = 0; r < rows; ++r) {
190 const float *row = in + size_t(r) * cols;
191 double sum = 0.0, sumsq = 0.0;
192 for (
int c = 0;
c < cols; ++
c) {
193 const double v = row[
c];
197 const double mean = sum / cols;
198 double var = sumsq / cols - mean * mean;
199 if (var < 0.0) var = 0.0;
200 const float inv = float(1.0 / std::sqrt(var + eps));
201 float *o = out + size_t(r) * cols;
202 for (
int c = 0;
c < cols; ++
c) {
203 float y = float(row[
c] - mean) * inv;
205 if (bias)
y += bias[
c];
211void rmsnorm(
const float *in,
int rows,
int cols,
const float *
scale,
float eps,
float *out) {
212 for (
int r = 0; r < rows; ++r) {
213 const float *row = in + size_t(r) * cols;
215 for (
int c = 0;
c < cols; ++
c) sumsq +=
double(row[
c]) * row[
c];
216 const float inv = float(1.0 / std::sqrt(sumsq / cols + eps));
217 float *o = out + size_t(r) * cols;
218 for (
int c = 0;
c < cols; ++
c) o[
c] = row[
c] * inv * (
scale ?
scale[
c] : 1.f);
224int convOutSize(
int inSize,
int kernel,
int stride,
int pad) {
225 return (inSize + 2 * pad - kernel) / stride + 1;
230void conv1d(
const float *
x,
const int *xDims,
const float *
w,
const int *wDims,
231 const float *bias,
int stride,
int pad,
float *out) {
232 const int N = xDims[0], C = xDims[1], L = xDims[2];
233 const int F = wDims[0], K = wDims[2];
234 const int OL = convOutSize(L, K, stride, pad);
235 for (
int n = 0;
n < N; ++
n) {
236 for (
int f = 0;
f < F; ++
f) {
237 for (
int ol = 0; ol < OL; ++ol) {
238 float acc = bias ? bias[
f] : 0.f;
239 for (
int c = 0;
c < C; ++
c) {
240 for (
int k = 0; k < K; ++k) {
241 const int il = ol * stride + k - pad;
242 if (il < 0 || il >= L)
continue;
243 acc +=
x[((
n * C +
c) * L + il)] *
w[((
f * C +
c) * K + k)];
246 out[(
n * F +
f) * OL + ol] = acc;
252void conv2d(
const float *
x,
const int *xDims,
const float *
w,
const int *wDims,
253 const float *bias,
int stride,
int pad,
float *out) {
254 const int N = xDims[0], C = xDims[1], H = xDims[2], W = xDims[3];
255 const int F = wDims[0], KH = wDims[2], KW = wDims[3];
256 const int OH = convOutSize(H, KH, stride, pad);
257 const int OW = convOutSize(W, KW, stride, pad);
258 for (
int n = 0;
n < N; ++
n) {
259 for (
int f = 0;
f < F; ++
f) {
260 for (
int oh = 0; oh < OH; ++oh) {
261 for (
int ow = 0; ow < OW; ++ow) {
262 float acc = bias ? bias[
f] : 0.f;
263 for (
int c = 0;
c < C; ++
c) {
264 for (
int kh = 0; kh < KH; ++kh) {
265 const int ih = oh * stride + kh - pad;
266 if (ih < 0 || ih >= H)
continue;
267 for (
int kw = 0; kw < KW; ++kw) {
268 const int iw = ow * stride + kw - pad;
269 if (iw < 0 || iw >= W)
continue;
270 acc +=
x[((
n * C +
c) * H + ih) * W + iw] *
271 w[((
f * C +
c) * KH + kh) * KW + kw];
275 out[((
n * F +
f) * OH + oh) * OW + ow] = acc;
282void maxpool2d(
const float *in,
const int *dims,
int ksize,
int stride,
int pad,
float *out) {
283 const int N = dims[0], C = dims[1], H = dims[2], W = dims[3];
284 const int OH = convOutSize(H, ksize, stride, pad);
285 const int OW = convOutSize(W, ksize, stride, pad);
286 for (
int n = 0;
n < N; ++
n)
287 for (
int c = 0;
c < C; ++
c)
288 for (
int oh = 0; oh < OH; ++oh)
289 for (
int ow = 0; ow < OW; ++ow) {
290 float best = -std::numeric_limits<float>::infinity();
291 for (
int kh = 0; kh < ksize; ++kh)
292 for (
int kw = 0; kw < ksize; ++kw) {
293 const int ih = oh * stride + kh - pad;
294 const int iw = ow * stride + kw - pad;
295 if (ih < 0 || ih >= H || iw < 0 || iw >= W)
continue;
296 best = std::max(best, in[((
n * C +
c) * H + ih) * W + iw]);
298 out[((
n * C +
c) * OH + oh) * OW + ow] = best;
302void avgpool2d(
const float *in,
const int *dims,
int ksize,
int stride,
int pad,
float *out) {
303 const int N = dims[0], C = dims[1], H = dims[2], W = dims[3];
304 const int OH = convOutSize(H, ksize, stride, pad);
305 const int OW = convOutSize(W, ksize, stride, pad);
306 for (
int n = 0;
n < N; ++
n)
307 for (
int c = 0;
c < C; ++
c)
308 for (
int oh = 0; oh < OH; ++oh)
309 for (
int ow = 0; ow < OW; ++ow) {
312 for (
int kh = 0; kh < ksize; ++kh)
313 for (
int kw = 0; kw < ksize; ++kw) {
314 const int ih = oh * stride + kh - pad;
315 const int iw = ow * stride + kw - pad;
316 if (ih < 0 || ih >= H || iw < 0 || iw >= W)
continue;
317 acc += in[((
n * C +
c) * H + ih) * W + iw];
320 out[((
n * C +
c) * OH + oh) * OW + ow] =
325void embedding(
const float *table,
int vocab,
int dim,
const float *indices,
int count,
327 for (
int r = 0; r < count; ++r) {
328 int idx = int(indices[r]);
329 idx = std::max(0, std::min(
idx, vocab - 1));
330 const float *row = table + size_t(
idx) * dim;
331 float *o = out + size_t(r) * dim;
332 for (
int d = 0;
d < dim; ++
d) o[
d] = row[
d];
336void concat(
const float *
const *ins,
const int *
const *inDims,
const int *inRanks,
int n,
337 int axis,
float *out,
const int *outDims,
int outRank) {
338 axis = normalizeAxis(axis, outRank);
341 for (
int k = 0; k <
n; ++k) {
342 starts[k] = axisSize;
343 axisSize += inDims[k][axis];
345 int outer = 1, inner = 1;
346 for (
int k = 0; k < axis; ++k) outer *= outDims[k];
347 for (
int k = axis + 1; k < outRank; ++k) inner *= outDims[k];
348 const int count = outer * axisSize * inner;
349 for (
int o = 0; o < count; ++o) {
350 const int ax = (o / inner) % axisSize;
351 const int outerPart = o / (axisSize * inner);
352 const int innerPart = o % inner;
354 for (
int k = 0; k <
n; ++k) {
355 const int sz = inDims[k][axis];
356 if (ax >= starts[k] && ax < starts[k] + sz) {
357 v = ins[k][(outerPart * sz + (ax - starts[k])) * inner + innerPart];
365void sliceOp(
const float *in,
const int *inDims,
int inRank,
int axis,
int begin,
int end,
366 float *out,
const int *outDims,
int outRank) {
368 axis = normalizeAxis(axis, outRank);
369 const int axisSize = end - begin;
370 int outer = 1, inner = 1;
371 for (
int k = 0; k < axis; ++k) outer *= outDims[k];
372 for (
int k = axis + 1; k < outRank; ++k) inner *= outDims[k];
373 const int count = outer * axisSize * inner;
374 for (
int o = 0; o < count; ++o) {
375 const int ax = (o / inner) % axisSize;
376 const int outerPart = o / (axisSize * inner);
377 const int innerPart = o % inner;
378 out[o] = in[(outerPart * inDims[axis] + (ax + begin)) * inner + innerPart];
382void permute(
const float *in,
const int *inDims,
int rank,
const int *order,
float *out,
383 const int *outDims) {
386 for (
int k = rank - 2; k >= 0; --k) S[k] = S[k + 1] * outDims[k + 1];
388 inStride[rank - 1] = 1;
389 for (
int k = rank - 2; k >= 0; --k) inStride[k] = inStride[k + 1] * inDims[k + 1];
390 const int count = product(outDims, rank);
391 for (
int o = 0; o < count; ++o) {
393 for (
int k = 0; k < rank; ++k) {
394 const int coord = (o / S[k]) % outDims[k];
395 idx += coord * inStride[order[k]];
402 const int *outDims,
int outRank) {
405 axis = normalizeAxis(axis, rank);
406 int outer = 1,
reduce = 1, inner = 1;
407 for (
int k = 0; k < axis; ++k) outer *= dims[k];
409 for (
int k = axis + 1; k < rank; ++k) inner *= dims[k];
410 const int rows = outer * inner;
411 const float negInf = -std::numeric_limits<float>::infinity();
412 const float posInf = std::numeric_limits<float>::infinity();
413 for (
int r = 0; r < rows; ++r) {
414 const int o = r / inner;
415 const int i = r % inner;
417 for (
int j = 0; j <
reduce; ++j) {
418 const float v = in[(o *
reduce + j) * inner + i];
424 out[o * inner + i] = acc;
428void argmax(
const float *in,
const int *dims,
int rank,
int axis,
float *out,
429 const int *outDims,
int outRank) {
432 axis = normalizeAxis(axis, rank);
433 int outer = 1,
reduce = 1, inner = 1;
434 for (
int k = 0; k < axis; ++k) outer *= dims[k];
436 for (
int k = axis + 1; k < rank; ++k) inner *= dims[k];
437 const int rows = outer * inner;
438 for (
int r = 0; r < rows; ++r) {
439 const int o = r / inner;
440 const int i = r % inner;
441 float best = -std::numeric_limits<float>::infinity();
443 for (
int j = 0; j <
reduce; ++j) {
444 const float v = in[(o *
reduce + j) * inner + i];
450 out[o * inner + i] = float(bestJ);
454void sdpa(
const float *q,
const float *k,
const float *
v,
const float *mask,
int B,
int H,
455 int T,
int S,
int D,
float scale,
float *out) {
456 for (
int b = 0;
b < B; ++
b) {
457 for (
int h = 0;
h < H; ++
h) {
458 for (
int t = 0; t < T; ++t) {
459 std::vector<float> scores(
static_cast<size_t>(S));
460 const float *qRow = q + (size_t(
b) * H +
h) * T * D +
size_t(t) * D;
461 for (
int s = 0;
s < S; ++
s) {
462 const float *kRow = k + (size_t(
b) * H +
h) * S * D +
size_t(
s) * D;
464 for (
int d = 0;
d < D; ++
d) acc +=
double(qRow[
d]) * kRow[
d];
465 float score = float(acc) *
scale;
466 if (mask) score += mask[(size_t(
b) * H +
h) * T * S +
size_t(t) * S +
s];
467 scores[
static_cast<size_t>(
s)] = score;
469 float mx = -std::numeric_limits<float>::infinity();
470 for (
int s = 0; s < S; ++s) mx = std::max(mx, scores[static_cast<size_t>(
s)]);
472 for (
int s = 0;
s < S; ++
s)
473 sum += std::exp(
double(scores[
static_cast<size_t>(
s)] - mx));
474 float *oRow = out + (size_t(
b) * H +
h) * T * D +
size_t(t) * D;
475 for (
int d = 0;
d < D; ++
d) {
477 for (
int s = 0;
s < S; ++
s) {
478 const float *vRow =
v + (size_t(
b) * H +
h) * S * D +
size_t(
s) * D;
479 acc += std::exp(
double(scores[
static_cast<size_t>(
s)] - mx)) * vRow[
d];
481 oRow[
d] = float(acc / sum);
488void resize2d(
const float *in,
const int *inDims,
int outW,
int outH,
int mode,
float *out) {
489 const int N = inDims[0], C = inDims[1], H = inDims[2], W = inDims[3];
490 for (
int n = 0;
n < N; ++
n) {
491 for (
int c = 0;
c < C; ++
c) {
492 for (
int oh = 0; oh < outH; ++oh) {
493 for (
int ow = 0; ow < outW; ++ow) {
494 const float *src = in + (size_t(
n) * C +
c) * H * W;
497 const int ih = std::min(
int(
float(oh) * H / outH), H - 1);
498 const int iw = std::min(
int(
float(ow) * W / outW), W - 1);
499 value = src[size_t(ih) * W + iw];
501 float x = float(ow + 0.5f) * W / outW - 0.5f;
502 float y = float(oh + 0.5f) * H / outH - 0.5f;
503 x = std::clamp(
x, 0.f,
float(W - 1));
504 y = std::clamp(
y, 0.f,
float(H - 1));
505 const int x0 = int(std::floor(
x));
506 const int y0 = int(std::floor(
y));
507 const int x1 = std::min(x0 + 1, W - 1);
508 const int y1 = std::min(y0 + 1, H - 1);
509 const float fx =
x - float(x0);
510 const float fy =
y - float(y0);
511 const float v00 = src[size_t(y0) * W + x0];
512 const float v10 = src[size_t(y0) * W + x1];
513 const float v01 = src[size_t(y1) * W + x0];
514 const float v11 = src[size_t(y1) * W + x1];
515 value = (v00 * (1.f - fx) + v10 * fx) * (1.f - fy) +
516 (v01 * (1.f - fx) + v11 * fx) * fy;
518 out[((size_t(
n) * C +
c) * outH + oh) * outW + ow] =
value;
gpgpu::ComputeShader * reduce
static constexpr int kMaxRank
void maxpool2d(const float *in, const int *dims, int ksize, int stride, int pad, float *out)
void softmax(const float *in, const int *dims, int rank, int axis, bool logMode, float *out)
void conv2d(const float *x, const int *xDims, const float *w, const int *wDims, const float *bias, int stride, int pad, float *out)
void sdpa(const float *q, const float *k, const float *v, const float *mask, int B, int H, int T, int S, int D, float scale, float *out)
void sliceOp(const float *in, const int *inDims, int inRank, int axis, int begin, int end, float *out, const int *outDims, int outRank)
void embedding(const float *table, int vocab, int dim, const float *indices, int count, float *out)
void avgpool2d(const float *in, const int *dims, int ksize, int stride, int pad, float *out)
void binaryOp(OpType type, const float *a, const int *aDims, int aRank, const float *b, const int *bDims, int bRank, float *out, const int *outDims, int outRank)
void permute(const float *in, const int *inDims, int rank, const int *order, float *out, const int *outDims)
void reduceAxis(OpType type, const float *in, const int *dims, int rank, int axis, float *out, const int *outDims, int outRank)
bool broadcastShape(const int *aDims, int aRank, const int *bDims, int bRank, int *outDims, int &outRank)
bool isElementwiseOp(OpType t)
void argmax(const float *in, const int *dims, int rank, int axis, float *out, const int *outDims, int outRank)
void layernorm(const float *in, int rows, int cols, const float *scale, const float *bias, float eps, float *out)
void rmsnorm(const float *in, int rows, int cols, const float *scale, float eps, float *out)
void concat(const float *const *ins, const int *const *inDims, const int *inRanks, int n, int axis, float *out, const int *outDims, int outRank)
float applyUnary(OpType t, float x, float s0, float s1)
void conv1d(const float *x, const int *xDims, const float *w, const int *wDims, const float *bias, int stride, int pad, float *out)
void unaryOp(OpType type, const float *in, int count, float *out, float s0, float s1)
void resize2d(const float *in, const int *inDims, int outW, int outH, int mode, float *out)