{ "domain": "com.microsoft", "name": "BiasSoftmax", "sinceVersion": 1, "description": "Computes `softmax(data + bias)` over the flattened suffix beginning at `axis`. The required `is_inner_broadcast` attribute selects how bias rows are reused: consecutive groups for inner broadcast or cyclic groups for outer broadcast. This specializes the `softmax(scores + additive_mask)` pattern used by transformer attention. Float16 and float32 are supported; the schema's double type is not.", "inputs": [ { "role": "data", "dtype": "T", "description": "The input data tensor." }, { "role": "bias", "dtype": "T", "description": "The bias (or additive mask) tensor. Its element count must be an integral number of flattened softmax rows and that row count must divide the data row count." } ], "outputs": [ { "role": "output", "dtype": "T", "rank": "ranks.data", "shape": "shapes.data", "description": "The output tensor; same shape as data." } ], "attributes": { "axis": 1 }, "attributeConstraints": { "is_inner_broadcast": { "required": true } }, "attributeDescriptions": { "axis": "The axis from which softmax is applied; dimensions from `axis` onward are included in the softmax reduction.", "is_inner_broadcast": "When 1, bias is broadcast across dimensions from `broadcast_axis` to `axis-1`; when 0, bias is broadcast across dimensions 0 to `broadcast_axis-1`." }, "typeConstraints": { "T": ["float32", "float16"] }, "args": { "data": { "kind": "tensor", "semantic": "data", "role": "input" }, "bias": { "kind": "tensor", "semantic": "bias", "role": "input" }, "output": { "kind": "tensor", "semantic": "output", "role": "output" } }, "derive": { "axisNorm": "attrs.axis if attrs.axis >= 0 else attrs.axis + ranks.data", "batchCount": "outer(shapes.data, axisNorm)", "blockSize": "dim(shapes.data, axisNorm) * inner(shapes.data, axisNorm)", "biasBlockCount": "numel(shapes.bias) / max(1, blockSize)", "biasContract": "(numel(shapes.data) == 0 and numel(shapes.bias) == 0) or (blockSize > 0 and biasBlockCount > 0 and numel(shapes.bias) % blockSize == 0 and biasBlockCount <= batchCount and batchCount % biasBlockCount == 0)" }, "tunables": { "WORKGROUP_SIZE": 256, "BLOCK_COLS": 2048 }, "bindingSets": { "biasSoftmax": [ { "name": "data", "arg": "data", "semantic": "data", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" }, { "name": "bias", "arg": "bias", "semantic": "bias", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" }, { "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "$scalar" }, { "name": "params", "semantic": "kernel.params", "buffer": { "type": "uniform" }, "struct": { "name": "Params", "fields": [ { "name": "blockSize", "type": "u32", "value": "blockSize" }, { "name": "batchCount", "type": "u32", "value": "batchCount" } ] } } ] }, "variants": [ { "id": "longrow_split", "priority": 40, "when": ["numel(shapes.data) == numel(shapes.output)", "ranks.data >= 1", "attrs.axis + ranks.data >= 0", "attrs.axis < ranks.data", "biasContract", "blockSize >= 65536", "batchCount > 0", "batchCount <= device.limits.maxComputeWorkgroupsPerDimension", "ceilDiv(blockSize, tunables.BLOCK_COLS) <= device.limits.maxComputeWorkgroupsPerDimension", "f16Ok(dtypes.T)"], "constants": { "scalar": "dtypes.T", "usesF16": "dtypes.T == \"f16\"", "combineSubgroups": false }, "intermediates": [ { "id": "blockMax", "dtype": "float32", "shape": "[outer(shapes.data, axisNorm) * ceilDiv(dim(shapes.data, axisNorm) * inner(shapes.data, axisNorm), tunables.BLOCK_COLS)]" }, { "id": "blockSum", "dtype": "float32", "shape": "[outer(shapes.data, axisNorm) * ceilDiv(dim(shapes.data, axisNorm) * inner(shapes.data, axisNorm), tunables.BLOCK_COLS)]" }, { "id": "rowMax", "dtype": "float32", "shape": "[outer(shapes.data, axisNorm)]" }, { "id": "rowSum", "dtype": "float32", "shape": "[outer(shapes.data, axisNorm)]" } ], "passes": [ { "id": "block_stats", "name": "BiasSoftmax.LongRowBlockStats", "source": { "shader": "bias-softmax-longrow-stats.wgsl.jinja", "inputs": { "stage": "\"block\"", "isInnerBroadcast": "attrs.is_inner_broadcast != 0", "biasBlockCount": "max(1, biasBlockCount)", "innerRepeat": "max(1, batchCount / max(1, biasBlockCount))" } }, "bindings": [ { "name": "data", "arg": "data", "semantic": "data", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" }, { "name": "bias", "arg": "bias", "semantic": "bias", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" }, { "name": "blockMax", "semantic": "blockMax", "buffer": { "type": "storage" }, "elementType": "f32" }, { "name": "blockSum", "semantic": "blockSum", "buffer": { "type": "storage" }, "elementType": "f32" }, { "name": "params", "semantic": "kernel.params", "buffer": { "type": "uniform" }, "struct": { "name": "Params", "fields": [ { "name": "blockSize", "type": "u32", "value": "blockSize" }, { "name": "blocks", "type": "u32", "value": "ceilDiv(blockSize, tunables.BLOCK_COLS)" } ] } } ], "dispatch": { "x": "ceilDiv(blockSize, tunables.BLOCK_COLS)", "y": "batchCount" } }, { "id": "row_stats", "name": "BiasSoftmax.LongRowStats", "source": { "shader": "bias-softmax-longrow-stats.wgsl.jinja", "inputs": { "stage": "\"row\"" } }, "bindings": [ { "name": "blockMax", "semantic": "blockMax", "buffer": { "type": "read-only-storage" }, "elementType": "f32" }, { "name": "blockSum", "semantic": "blockSum", "buffer": { "type": "read-only-storage" }, "elementType": "f32" }, { "name": "rowMax", "semantic": "rowMax", "buffer": { "type": "storage" }, "elementType": "f32" }, { "name": "rowSum", "semantic": "rowSum", "buffer": { "type": "storage" }, "elementType": "f32" }, { "name": "params", "semantic": "kernel.params", "buffer": { "type": "uniform" }, "struct": { "name": "Params", "fields": [{ "name": "blocks", "type": "u32", "value": "ceilDiv(blockSize, tunables.BLOCK_COLS)" }] } } ], "dispatch": { "x": "batchCount" } }, { "id": "normalize", "name": "BiasSoftmax.LongRowNormalize", "source": { "shader": "bias-softmax-longrow-normalize.wgsl.jinja", "inputs": { "isInnerBroadcast": "attrs.is_inner_broadcast != 0", "biasBlockCount": "max(1, biasBlockCount)", "innerRepeat": "max(1, batchCount / max(1, biasBlockCount))" } }, "bindings": [ { "name": "data", "arg": "data", "semantic": "data", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" }, { "name": "bias", "arg": "bias", "semantic": "bias", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" }, { "name": "rowMax", "semantic": "rowMax", "buffer": { "type": "read-only-storage" }, "elementType": "f32" }, { "name": "rowSum", "semantic": "rowSum", "buffer": { "type": "read-only-storage" }, "elementType": "f32" }, { "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "$scalar" }, { "name": "params", "semantic": "kernel.params", "buffer": { "type": "uniform" }, "struct": { "name": "Params", "fields": [{ "name": "blockSize", "type": "u32", "value": "blockSize" }] } } ], "dispatch": { "x": "ceilDiv(blockSize, tunables.BLOCK_COLS)", "y": "batchCount" } } ] }, { "id": "packed_rows", "priority": 30, "when": ["numel(shapes.data) == numel(shapes.output)", "ranks.data >= 1", "attrs.axis + ranks.data >= 0", "attrs.axis < ranks.data", "biasContract", "blockSize > 0", "blockSize <= 8", "batchCount >= 64", "f16Ok(dtypes.T)"], "constants": { "scalar": "dtypes.T", "usesF16": "dtypes.T == \"f16\"", "combineSubgroups": false, "packedRows": true }, "passes": [ { "id": "main", "name": "BiasSoftmax.PackedRows", "source": { "shader": "bias-softmax.wgsl.jinja", "inputs": { "blockSize": "blockSize", "isInnerBroadcast": "attrs.is_inner_broadcast != 0", "biasBlockCount": "max(1, biasBlockCount)", "innerRepeat": "max(1, batchCount / max(1, biasBlockCount))" } }, "bindings": "biasSoftmax", "dispatch": { "gridStride": "batchCount", "workgroupSize": "tunables.WORKGROUP_SIZE" } } ] }, { "id": "adaptive_row", "priority": 10, "when": ["numel(shapes.data) == numel(shapes.output)", "ranks.data >= 1", "attrs.axis + ranks.data >= 0", "attrs.axis < ranks.data", "biasContract", "numel(shapes.data) >= 0", "f16Ok(dtypes.T)"], "constants": { "packedRows": false, "scalar": "dtypes.T", "usesF16": "dtypes.T == \"f16\"", "combineSubgroups": "device.features.has(\"subgroups\")" }, "passes": [ { "id": "main", "name": "BiasSoftmax.AdaptiveRow", "source": { "shader": "bias-softmax.wgsl.jinja", "inputs": { "blockSize": "blockSize", "isInnerBroadcast": "attrs.is_inner_broadcast != 0", "biasBlockCount": "max(1, biasBlockCount)", "innerRepeat": "max(1, batchCount / max(1, biasBlockCount))" } }, "bindings": "biasSoftmax", "dispatch": { "workgroups": "batchCount" } } ] } ] }