Files
2026-07-13 13:33:03 +08:00

213 lines
7.6 KiB
C++

// Copyright @ MNN
#include "MetalSoftmaxShader.hpp"
namespace MNN {
// Plane Softmax (scalar)
const char* gSoftmaxPlaneSrc = R"metal(
#include <metal_stdlib>
using namespace metal;
struct softmax_shape {
int inside_size;
int axis_length;
int outside_size;
int flat_length;
};
kernel void softmax_plane(const device T* in [[buffer(0)]],
device T* out [[buffer(1)]],
constant softmax_shape& s [[buffer(2)]],
uint2 gid [[thread_position_in_grid]]) {
if ((int)gid.x >= s.inside_size || (int)gid.y >= s.outside_size) return;
const int axis_off = int(gid.y) * s.axis_length * s.inside_size + int(gid.x);
const device T* axis_in = in + axis_off;
device T* axis_out = out + axis_off;
float maxv = -FLT_MAX;
for (int i = 0; i < s.axis_length; ++i) {
maxv = max(maxv, float(axis_in[i * s.inside_size]));
}
float sumv = 0.0f;
for (int i = 0; i < s.axis_length; ++i) {
sumv += exp(float(axis_in[i * s.inside_size]) - maxv);
}
for (int i = 0; i < s.axis_length; ++i) {
axis_out[i * s.inside_size] = (T)(exp(float(axis_in[i * s.inside_size]) - maxv) / sumv);
}
}
)metal";
// Plane Softmax with simd group reduce (scalar)
const char* gSoftmaxPlaneSgSrc = R"metal(
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct softmax_shape {
int inside_size;
int axis_length;
int outside_size;
int flat_length;
};
#define SIMD_GROUP_WIDTH 32
kernel void softmax_plane_sg(const device T* in [[buffer(0)]],
device T* out [[buffer(1)]],
constant softmax_shape& s [[buffer(2)]],
uint2 gid [[threadgroup_position_in_grid]],
uint tiisg [[thread_index_in_simdgroup]]) {
if ((int)gid.x >= s.inside_size || (int)gid.y >= s.outside_size) return;
const int axis_off = int(gid.y) * s.axis_length * s.inside_size + int(gid.x);
const device T* axis_in = in + axis_off;
device T* axis_out = out + axis_off;
float lmax = -FLT_MAX;
for (int i = tiisg; i < s.axis_length; i += SIMD_GROUP_WIDTH) {
lmax = max(lmax, float(axis_in[i * s.inside_size]));
}
float maxv = simd_max(lmax);
float lsum = 0.0f;
for (int i = tiisg; i < s.axis_length; i += SIMD_GROUP_WIDTH) {
lsum += exp(float(axis_in[i * s.inside_size]) - maxv);
}
float sumv = simd_sum(lsum);
for (int i = tiisg; i < s.axis_length; i += SIMD_GROUP_WIDTH) {
axis_out[i * s.inside_size] = (T)(exp(float(axis_in[i * s.inside_size]) - maxv) / sumv);
}
}
)metal";
// Plane Softmax with multi-simdgroup threadgroup reduction
const char* gSoftmaxPlaneSgTG = R"metal(
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct softmax_shape {
int inside_size;
int axis_length;
int outside_size;
int flat_length;
};
#define SIMD_GROUP_WIDTH 32
#ifndef TG_SIZE
#define TG_SIZE 128
#endif
#define SG_PER_TG (TG_SIZE / SIMD_GROUP_WIDTH)
kernel void softmax_plane_sg_tg(const device T* in [[buffer(0)]],
device T* out [[buffer(1)]],
constant softmax_shape& s [[buffer(2)]],
uint2 gtp [[threadgroup_position_in_grid]],
uint tiisg [[thread_index_in_simdgroup]],
uint sgitg [[simdgroup_index_in_threadgroup]]) {
if ((int)gtp.x >= s.inside_size || (int)gtp.y >= s.outside_size) return;
const int axis_off = int(gtp.y) * s.axis_length * s.inside_size + int(gtp.x);
const device T* axis_in = in + axis_off;
device T* axis_out = out + axis_off;
const int stride = SIMD_GROUP_WIDTH * SG_PER_TG;
int start = int(tiisg) + int(sgitg) * SIMD_GROUP_WIDTH;
// 1) Max reduction
float lmax = -FLT_MAX;
for (int i = start; i < s.axis_length; i += stride) {
lmax = max(lmax, float(axis_in[i * s.inside_size]));
}
float sgMax = simd_max(lmax);
threadgroup float tgMax[SG_PER_TG];
if (tiisg == 0) tgMax[sgitg] = sgMax;
threadgroup_barrier(mem_flags::mem_threadgroup);
threadgroup float finalMaxStore[1];
if (sgitg == 0 && tiisg == 0) {
float fm = -FLT_MAX;
for (int k = 0; k < SG_PER_TG; ++k) fm = max(fm, tgMax[k]);
finalMaxStore[0] = fm;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
float maxv = finalMaxStore[0];
// 2) Sum reduction
float lsum = 0.0f;
for (int i = start; i < s.axis_length; i += stride) {
lsum += exp(float(axis_in[i * s.inside_size]) - maxv);
}
float sgSum = simd_sum(lsum);
threadgroup float tgSum[SG_PER_TG];
if (tiisg == 0) tgSum[sgitg] = sgSum;
threadgroup_barrier(mem_flags::mem_threadgroup);
threadgroup float finalSumStore[1];
if (sgitg == 0 && tiisg == 0) {
float fs = 0.0f;
for (int k = 0; k < SG_PER_TG; ++k) fs += tgSum[k];
finalSumStore[0] = fs;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
float sumv = finalSumStore[0];
// 3) Write back
for (int i = start; i < s.axis_length; i += stride) {
axis_out[i * s.inside_size] = (T)(exp(float(axis_in[i * s.inside_size]) - maxv) / sumv);
}
}
)metal";
// Attention variant (uses ftype and axis_align_length)
const char* gSoftmaxSgReduce = R"metal(
#include <metal_stdlib>
using namespace metal;
struct softmax_shape {
int inside_size;
int axis_length;
int outside_size;
int axis_align_length;
};
#define SIMD_GROUP_WIDTH 32
kernel void softmax_plane(const device ftype *in [[buffer(0)]],
device ftype *out [[buffer(1)]],
constant softmax_shape& s [[buffer(2)]],
uint2 gid [[thread_position_in_grid]]) {
if ((int)gid.x >= s.inside_size || (int)gid.y >= s.outside_size) return;
auto in_offset = gid.y * s.axis_length * s.inside_size + gid.x;
auto out_offset = gid.y * s.axis_align_length * s.inside_size + gid.x;
auto axis_in = in + in_offset;
auto axis_out = out + out_offset;
float max1 = -FLT_MAX;
for (int i = 0; i < s.axis_length; i++) {
max1 = max(max1, float(axis_in[i * s.inside_size]));
}
float sum1 = 0;
for (int i = 0; i < s.axis_length; i++) {
sum1 += exp(float(axis_in[i * s.inside_size]) - float(max1));
}
for (int i = 0; i < s.axis_align_length; i++) {
axis_out[i * s.inside_size] = i >= s.axis_length ? ftype(0.0) : ftype(exp(float(axis_in[i * s.inside_size]) - float(max1)) / sum1);
}
}
kernel void softmax_plane_sg(const device ftype *in [[buffer(0)]],
device ftype *out [[buffer(1)]],
constant softmax_shape& s [[buffer(2)]],
uint2 gid[[threadgroup_position_in_grid]],
uint tiisg[[thread_index_in_simdgroup]],
uint sgitg[[simdgroup_index_in_threadgroup]]
) {
if ((int)gid.x >= s.inside_size || (int)gid.y >= s.outside_size) return;
auto in_offset = gid.y * s.axis_length * s.inside_size + gid.x;
auto out_offset = gid.y * s.axis_align_length * s.inside_size + gid.x;
auto axis_in = in + in_offset;
auto axis_out = out + out_offset;
float max1 = -FLT_MAX;
for (int i = tiisg; i < s.axis_length; i+=SIMD_GROUP_WIDTH) {
max1 = max(max1, float(axis_in[i * s.inside_size]));
}
max1 = simd_max(max1);
float sum1 = 0;
for (int i = tiisg; i < s.axis_length; i+=SIMD_GROUP_WIDTH) {
sum1 += exp(float(axis_in[i * s.inside_size]) - float(max1));
}
sum1 = simd_sum(sum1);
for (int i = tiisg; i < s.axis_align_length; i+=SIMD_GROUP_WIDTH) {
axis_out[i * s.inside_size] = i >= s.axis_length ? ftype(0.0) : ftype(exp(float(axis_in[i * s.inside_size]) - float(max1)) / sum1);
}
}
)metal";
}