216 lines
9.2 KiB
C++
216 lines
9.2 KiB
C++
#include <algorithm>
|
|
#include <cmath>
|
|
#include <sstream>
|
|
#include "common/Exception.h"
|
|
#include "tensor/KernelGenWgslInternal.h"
|
|
#include "tensor/Quant.h"
|
|
|
|
namespace eve::tensor::wgsl_detail {
|
|
void genSoftmax(const Graph &g, const FusedGroup &grp, KernelSpec &out) {
|
|
const GraphNode &sn = g.node(grp.outputNode);
|
|
const GraphNode &X = g.node(sn.in0);
|
|
const int axis = sn.i0;
|
|
int outer = 1, reduce = 1, inner = 1;
|
|
for (int k = 0; k < axis; ++k) outer *= X.dims[k];
|
|
reduce = X.dims[axis];
|
|
for (int k = axis + 1; k < X.rank; ++k) inner *= X.dims[k];
|
|
const int rows = outer * inner;
|
|
const bool logMode = grp.logMode;
|
|
|
|
std::ostringstream os1, os2;
|
|
os1 << header(kLocalSize);
|
|
os1 << bufferDecl(0, "in_");
|
|
os1 << bufferDecl(1, "mx");
|
|
os1 << bufferDecl(2, "sm");
|
|
os1 << pushConstant();
|
|
os1 << "@compute @workgroup_size(workgroupX, workgroupY)\nfn main(@builtin(global_invocation_id) globalId: "
|
|
"vec3<u32>, @builtin(local_invocation_id) localId: vec3<u32>, @builtin(workgroup_id) groupId: vec3<u32>) "
|
|
"{\n";
|
|
os1 << " var i_: u32 = globalId.x;\n";
|
|
os1 << " if (i_ >= " << rows << "u) { return; }\n";
|
|
os1 << " var o_: u32 = i_ / " << inner << "u;\n";
|
|
os1 << " var ii: u32 = i_ % " << inner << "u;\n";
|
|
os1 << " var m: f32 = -3.402823e38;\n";
|
|
os1 << " for (var j: u32 = 0u; j < " << reduce << "u; j++) {\n";
|
|
os1 << " m = max(m, in_[(o_ * " << reduce << "u + j) * " << inner << "u + ii]);\n";
|
|
os1 << " }\n";
|
|
os1 << " var s: f32 = 0.0;\n";
|
|
os1 << " for (var j: u32 = 0u; j < " << reduce << "u; j++) {\n";
|
|
os1 << " s += exp(in_[(o_ * " << reduce << "u + j) * " << inner << "u + ii] - m);\n";
|
|
os1 << " }\n";
|
|
os1 << " mx[i_] = m;\n";
|
|
os1 << " sm[i_] = s;\n";
|
|
os1 << "}\n";
|
|
|
|
os2 << header(kLocalSize);
|
|
os2 << bufferDecl(0, "in_");
|
|
os2 << bufferDecl(1, "mx");
|
|
os2 << bufferDecl(2, "sm");
|
|
os2 << bufferDecl(3, "o");
|
|
os2 << pushConstant();
|
|
os2 << "@compute @workgroup_size(workgroupX, workgroupY)\nfn main(@builtin(global_invocation_id) globalId: "
|
|
"vec3<u32>, @builtin(local_invocation_id) localId: vec3<u32>, @builtin(workgroup_id) groupId: vec3<u32>) "
|
|
"{\n";
|
|
os2 << " var i_: u32 = globalId.x;\n";
|
|
os2 << " if (i_ >= " << sn.size << "u) { return; }\n";
|
|
os2 << " var o_: u32 = (i_ / (" << reduce * inner << "u)) * " << inner << "u + (i_ % " << inner << "u);\n";
|
|
os2 << " var m: f32 = mx[o_];\n";
|
|
os2 << " var s: f32 = sm[o_];\n";
|
|
os2 << " var x: f32 = in_[i_];\n";
|
|
if (logMode) {
|
|
os2 << " o[i_] = (x - m) - log(s);\n";
|
|
} else {
|
|
os2 << " o[i_] = exp(x - m) / s;\n";
|
|
}
|
|
os2 << "}\n";
|
|
out.pass1 = os1.str();
|
|
out.pass2 = os2.str();
|
|
out.groupsX1 = groupsFor(rows);
|
|
out.groupsX2 = groupsFor(sn.size);
|
|
out.inputCount = 1;
|
|
out.inputsReadPass1 = 1;
|
|
out.statsCount = 2;
|
|
out.statsSize = rows;
|
|
out.twoPass = true;
|
|
return;
|
|
}
|
|
|
|
void genNorm(const Graph &g, const FusedGroup &grp, bool rms, KernelSpec &out) {
|
|
const GraphNode &nn = g.node(grp.outputNode);
|
|
const GraphNode &X = g.node(nn.in0);
|
|
const int cols = X.dims[X.rank - 1];
|
|
const int rows = X.size / cols;
|
|
const float eps = nn.s0;
|
|
const bool hasScale = grp.hasScale;
|
|
const bool hasBias = !rms && grp.hasBias;
|
|
const int inputCount = 1 + (hasScale ? 1 : 0) + (hasBias ? 1 : 0);
|
|
const int statsCount = rms ? 1 : 2;
|
|
const int outBinding = inputCount + statsCount;
|
|
|
|
std::ostringstream os1, os2;
|
|
os1 << header(kLocalSize);
|
|
os1 << bufferDecl(0, "in_");
|
|
os1 << bufferDecl(1, "st0");
|
|
if (statsCount > 1) os1 << bufferDecl(2, "st1");
|
|
os1 << pushConstant();
|
|
os1 << "@compute @workgroup_size(workgroupX, workgroupY)\nfn main(@builtin(global_invocation_id) globalId: "
|
|
"vec3<u32>, @builtin(local_invocation_id) localId: vec3<u32>, @builtin(workgroup_id) groupId: vec3<u32>) "
|
|
"{\n";
|
|
os1 << " var i_: u32 = globalId.x;\n";
|
|
os1 << " if (i_ >= " << rows << "u) { return; }\n";
|
|
os1 << " var s0: f32 = 0.0;\n";
|
|
if (statsCount > 1) os1 << " var s1: f32 = 0.0;\n";
|
|
os1 << " for (var j: u32 = 0u; j < " << cols << "u; j++) {\n";
|
|
os1 << " var v: f32 = in_[i_ * " << cols << "u + j];\n";
|
|
if (rms) {
|
|
os1 << " s0 += v * v;\n";
|
|
} else {
|
|
os1 << " s0 += v; s1 += v * v;\n";
|
|
}
|
|
os1 << " }\n";
|
|
os1 << " st0[i_] = s0;\n";
|
|
if (statsCount > 1) os1 << " st1[i_] = s1;\n";
|
|
os1 << "}\n";
|
|
|
|
os2 << header(kLocalSize);
|
|
for (int k = 0; k < inputCount; ++k) os2 << bufferDecl(k, ("a" + std::to_string(k)).c_str());
|
|
os2 << bufferDecl(inputCount, "st0");
|
|
if (statsCount > 1) os2 << bufferDecl(inputCount + 1, "st1");
|
|
os2 << bufferDecl(outBinding, "o");
|
|
os2 << pushConstant();
|
|
os2 << "@compute @workgroup_size(workgroupX, workgroupY)\nfn main(@builtin(global_invocation_id) globalId: "
|
|
"vec3<u32>, @builtin(local_invocation_id) localId: vec3<u32>, @builtin(workgroup_id) groupId: vec3<u32>) "
|
|
"{\n";
|
|
os2 << " var i_: u32 = globalId.x;\n";
|
|
os2 << " if (i_ >= " << nn.size << "u) { return; }\n";
|
|
os2 << " var r: u32 = i_ / " << cols << "u;\n";
|
|
os2 << " var c: u32 = i_ % " << cols << "u;\n";
|
|
if (rms) {
|
|
os2 << " var inv: f32 = 1.0 / sqrt(st0[r] / " << cols << ".0 + " << scalarStr(eps) << ");\n";
|
|
os2 << " var y: f32 = a0[i_] * inv;\n";
|
|
if (hasScale) os2 << " y *= a1[c];\n";
|
|
} else {
|
|
os2 << " var mean: f32 = st0[r] / " << cols << ".0;\n";
|
|
os2 << " var variance: f32 = st1[r] / " << cols << ".0 - mean * mean;\n";
|
|
os2 << " variance = max(variance, 0.0);\n";
|
|
os2 << " var inv: f32 = 1.0 / sqrt(variance + " << scalarStr(eps) << ");\n";
|
|
os2 << " var y: f32 = (a0[i_] - mean) * inv;\n";
|
|
if (hasScale) os2 << " y *= a1[c];\n";
|
|
if (hasBias) os2 << " y += a" << (hasScale ? 2 : 1) << "[c];\n";
|
|
}
|
|
os2 << " o[i_] = y;\n";
|
|
os2 << "}\n";
|
|
out.pass1 = os1.str();
|
|
out.pass2 = os2.str();
|
|
out.groupsX1 = groupsFor(rows);
|
|
out.groupsX2 = groupsFor(nn.size);
|
|
out.inputCount = inputCount;
|
|
out.inputsReadPass1 = 1;
|
|
out.statsCount = statsCount;
|
|
out.statsSize = rows;
|
|
out.twoPass = true;
|
|
return;
|
|
}
|
|
|
|
void genReduceOrArgmax(const Graph &g, const FusedGroup &grp, bool argmax, KernelSpec &out) {
|
|
const GraphNode &rn = g.node(grp.outputNode);
|
|
const GraphNode &X = g.node(rn.in0);
|
|
const int axis = rn.i0;
|
|
int outer = 1, reduce = 1, inner = 1;
|
|
for (int k = 0; k < axis; ++k) outer *= X.dims[k];
|
|
reduce = X.dims[axis];
|
|
for (int k = axis + 1; k < X.rank; ++k) inner *= X.dims[k];
|
|
const int outSize = outer * inner;
|
|
std::ostringstream os;
|
|
os << header(kLocalSize);
|
|
os << bufferDecl(0, "in_");
|
|
os << bufferDecl(1, "o");
|
|
os << pushConstant();
|
|
os << "@compute @workgroup_size(workgroupX, workgroupY)\nfn main(@builtin(global_invocation_id) globalId: "
|
|
"vec3<u32>, @builtin(local_invocation_id) localId: vec3<u32>, @builtin(workgroup_id) groupId: vec3<u32>) {\n";
|
|
os << " var i_: u32 = globalId.x;\n";
|
|
os << " if (i_ >= " << outSize << "u) { return; }\n";
|
|
os << " var o_: u32 = i_ / " << inner << "u;\n";
|
|
os << " var ii: u32 = i_ % " << inner << "u;\n";
|
|
if (argmax) {
|
|
os << " var best: f32 = -3.402823e38;\n";
|
|
os << " var bestJ: f32 = 0.0;\n";
|
|
os << " for (var j: u32 = 0u; j < " << reduce << "u; j++) {\n";
|
|
os << " var v: f32 = in_[(o_ * " << reduce << "u + j) * " << inner << "u + ii];\n";
|
|
os << " if (v > best) { best = v; bestJ = f32(j); }\n";
|
|
os << " }\n";
|
|
os << " o[i_] = bestJ;\n";
|
|
} else {
|
|
switch (grp.op) {
|
|
case OpType::ReduceSum:
|
|
case OpType::ReduceMean:
|
|
os << " var acc: f32 = 0.0;\n";
|
|
os << " for (var j: u32 = 0u; j < " << reduce << "u; j++) { acc += in_[(o_ * " << reduce << "u + j) * "
|
|
<< inner << "u + ii]; }\n";
|
|
if (grp.op == OpType::ReduceMean) os << " acc /= " << scalarStr(float(reduce)) << ";\n";
|
|
break;
|
|
case OpType::ReduceMin:
|
|
os << " var acc: f32 = 3.402823e38;\n";
|
|
os << " for (var j: u32 = 0u; j < " << reduce << "u; j++) { acc = min(acc, in_[(o_ * " << reduce
|
|
<< "u + j) * " << inner << "u + ii]); }\n";
|
|
break;
|
|
case OpType::ReduceMax:
|
|
os << " var acc: f32 = -3.402823e38;\n";
|
|
os << " for (var j: u32 = 0u; j < " << reduce << "u; j++) { acc = max(acc, in_[(o_ * " << reduce
|
|
<< "u + j) * " << inner << "u + ii]); }\n";
|
|
break;
|
|
default: throw eve::Exception("Tensor WGSL: unsupported kernel variant or binding count");
|
|
}
|
|
os << " o[i_] = acc;\n";
|
|
}
|
|
os << "}\n";
|
|
out.pass1.clear();
|
|
out.pass2 = os.str();
|
|
out.groupsX2 = groupsFor(outSize);
|
|
out.inputCount = 1;
|
|
return;
|
|
}
|
|
|
|
|
|
} // namespace eve::tensor::wgsl_detail
|