Xenova's picture
Xenova HF Staff
sync 2e7068faf55e
bbc3807 verified
Raw
History Blame
15.9 kB
{
"domain": "ai.onnx",
"name": "DynamicQuantizeLinear",
"sinceVersion": 11,
"description": "Computes a per-tensor scale and zero point from the range of floating-point input `x`, extending the range to include zero, then quantizes each value to `uint8` as `saturate(round(x / y_scale) + y_zero_point)`. Uses round-to-nearest-even and clamps results to `[0, 255]`.",
"inputs": [{ "role": "x", "dtype": "T", "description": "Float32 input tensor to quantize." }],
"outputs": [
{
"role": "y",
"dtype": "TQ",
"rank": "ranks.x",
"description": "Quantized output tensor; same shape as the input.",
"shape": "shapes.x"
},
{
"role": "y_scale",
"dtype": "T",
"rank": 0,
"description": "Per-tensor scale factor derived from the input min/max range; scalar.",
"shape": []
},
{
"role": "y_zero_point",
"dtype": "TQ",
"rank": 0,
"description": "Per-tensor zero point for the quantization; scalar.",
"shape": []
}
],
"typeConstraints": { "T": ["float32"], "TQ": ["uint8"] },
"args": {
"x": { "kind": "tensor", "semantic": "x", "role": "input" },
"y": { "kind": "tensor", "semantic": "y", "role": "output" },
"y_scale": { "kind": "tensor", "semantic": "y_scale", "role": "output" },
"y_zero_point": { "kind": "tensor", "semantic": "y_zero_point", "role": "output" }
},
"tunables": {
"WORKGROUP_SIZE": 256,
"ELEMENTS_PER_THREAD": 4,
"GRID_STRIDE_MIN_ELEMENTS": 262144,
"MAX_GRID_PARTIALS": 256,
"SERIAL_MAX_ELEMENTS": 8192
},
"tunableDescriptions": {
"GRID_STRIDE_MIN_ELEMENTS": "Sets the minimum input element count admitted to the bounded grid-stride reduction.",
"MAX_GRID_PARTIALS": "Caps the number of min/max partials emitted by the grid-stride reduction, bounding scratch and finalization work.",
"SERIAL_MAX_ELEMENTS": "Sets the input-size ceiling that prefers the one-invocation route; larger inputs use it only when the parallel reduction cannot fit."
},
"derive": {
"deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
"storageBufferLimit": "min(device.limits.maxStorageBufferBindingSize, device.limits.maxBufferSize)",
"inputCount": "numel(shapes.x)",
"elementsPerBlock": "tunables.WORKGROUP_SIZE * tunables.ELEMENTS_PER_THREAD",
"fullPartials": "ceilDiv(inputCount, elementsPerBlock)",
"gridPartials": "min(tunables.MAX_GRID_PARTIALS, fullPartials)",
"baseContract": "ranks.x >= 1 and ranks.y == ranks.x and inputCount == numel(shapes.y) and ranks.y_scale == 0 and ranks.y_zero_point == 0",
"serialContract": "ranks.x >= 0 and ranks.y == ranks.x and inputCount == numel(shapes.y) and ranks.y_scale == 0 and ranks.y_zero_point == 0",
"parallelDeviceOk": "tunables.WORKGROUP_SIZE <= deviceWorkgroupCap and 8 * tunables.WORKGROUP_SIZE <= device.limits.maxComputeWorkgroupStorageSize",
"fullScratchFits": "4 * fullPartials <= storageBufferLimit",
"gridScratchFits": "4 * gridPartials <= storageBufferLimit",
"fullDispatchFits": "ceilDiv(fullPartials, device.limits.maxComputeWorkgroupsPerDimension) <= device.limits.maxComputeWorkgroupsPerDimension",
"gridDispatchFits": "gridPartials <= device.limits.maxComputeWorkgroupsPerDimension",
"parallelFullFits": "parallelDeviceOk and fullScratchFits and fullDispatchFits",
"parallelGridFits": "parallelDeviceOk and gridScratchFits and gridDispatchFits and fullDispatchFits",
"serialFallbackNeeded": "inputCount <= tunables.SERIAL_MAX_ELEMENTS or not parallelFullFits"
},
"bindingSets": {
"reduce": [
{
"name": "x",
"arg": "x",
"semantic": "x",
"buffer": { "type": "read-only-storage" },
"elementType": "$inputElement"
},
{ "name": "partial_min", "semantic": "partial_min", "buffer": { "type": "storage" }, "elementType": "f32" },
{ "name": "partial_max", "semantic": "partial_max", "buffer": { "type": "storage" }, "elementType": "f32" },
{
"name": "params",
"semantic": "kernel.params",
"buffer": { "type": "uniform" },
"struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.x)" }] }
}
],
"finalizeFull": [
{
"name": "partial_min",
"semantic": "partial_min",
"buffer": { "type": "read-only-storage" },
"elementType": "f32"
},
{
"name": "partial_max",
"semantic": "partial_max",
"buffer": { "type": "read-only-storage" },
"elementType": "f32"
},
{
"name": "y_scale",
"arg": "y_scale",
"semantic": "y_scale",
"buffer": { "type": "storage" },
"elementType": "f32",
"length": 1
},
{
"name": "y_zero_point",
"arg": "y_zero_point",
"semantic": "y_zero_point",
"buffer": { "type": "storage" },
"elementType": "u32",
"length": 1
},
{
"name": "params",
"semantic": "kernel.params",
"buffer": { "type": "uniform" },
"struct": { "name": "Params", "fields": [{ "name": "numPartials", "type": "u32", "value": "fullPartials" }] }
}
],
"finalizeGrid": [
{
"name": "partial_min",
"semantic": "partial_min",
"buffer": { "type": "read-only-storage" },
"elementType": "f32"
},
{
"name": "partial_max",
"semantic": "partial_max",
"buffer": { "type": "read-only-storage" },
"elementType": "f32"
},
{
"name": "y_scale",
"arg": "y_scale",
"semantic": "y_scale",
"buffer": { "type": "storage" },
"elementType": "f32",
"length": 1
},
{
"name": "y_zero_point",
"arg": "y_zero_point",
"semantic": "y_zero_point",
"buffer": { "type": "storage" },
"elementType": "u32",
"length": 1
},
{
"name": "params",
"semantic": "kernel.params",
"buffer": { "type": "uniform" },
"struct": { "name": "Params", "fields": [{ "name": "numPartials", "type": "u32", "value": "gridPartials" }] }
}
],
"quantize": [
{
"name": "x",
"arg": "x",
"semantic": "x",
"buffer": { "type": "read-only-storage" },
"elementType": "$inputElement"
},
{
"name": "y_scale",
"arg": "y_scale",
"semantic": "y_scale",
"buffer": { "type": "read-only-storage" },
"elementType": "f32",
"length": 1
},
{
"name": "y_zero_point",
"arg": "y_zero_point",
"semantic": "y_zero_point",
"buffer": { "type": "read-only-storage" },
"elementType": "u32",
"length": 1
},
{ "name": "y", "arg": "y", "semantic": "y", "buffer": { "type": "storage" }, "elementType": "u32" },
{
"name": "params",
"semantic": "kernel.params",
"buffer": { "type": "uniform" },
"struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.x)" }] }
}
],
"serial": [
{ "name": "x", "arg": "x", "semantic": "x", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
{ "name": "y", "arg": "y", "semantic": "y", "buffer": { "type": "storage" }, "elementType": "u32" },
{
"name": "y_scale",
"arg": "y_scale",
"semantic": "y_scale",
"buffer": { "type": "storage" },
"elementType": "f32",
"length": 1
},
{
"name": "y_zero_point",
"arg": "y_zero_point",
"semantic": "y_zero_point",
"buffer": { "type": "storage" },
"elementType": "u32",
"length": 1
},
{
"name": "params",
"semantic": "kernel.params",
"buffer": { "type": "uniform" },
"struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.x)" }] }
}
]
},
"variants": [
{
"id": "single_invocation",
"description": "Uses one invocation to find the range and quantize the tensor, avoiding partial buffers for small inputs. It also provides the fallback when the parallel reduction cannot satisfy device limits.",
"when": ["serialContract", "serialFallbackNeeded"],
"passes": [
{
"id": "main",
"name": "DynamicQuantizeLinear",
"source": { "shader": "dynamic-quantize-linear.wgsl.jinja", "inputs": { "fromPartials": false } },
"bindings": "serial",
"dispatch": { "x": 1 }
}
]
},
{
"id": "parallel_subgroup_reduce_vec4",
"description": "Reduces independent input blocks to min/max partials, combines them, and quantizes in a separate pass. The family uses packed reads when the input length is vec4-aligned.",
"priority": 11,
"when": ["baseContract", "inputCount > 0", "inputCount % 4 == 0", "parallelFullFits"],
"constants": {
"workgroupSize": "tunables.WORKGROUP_SIZE",
"elemsPerThread": "tunables.ELEMENTS_PER_THREAD",
"vec4": true,
"inputElement": "\"vec4<f32>\"",
"useSubgroups": "device.features.has(\"subgroups\")"
},
"intermediates": [
{ "id": "partial_min", "dtype": "float32", "shape": "[fullPartials]" },
{ "id": "partial_max", "dtype": "float32", "shape": "[fullPartials]" }
],
"passes": [
{
"id": "reduce",
"name": "DynamicQuantizeLinear.ReduceMinMax",
"shader": "dynamic-quantize-linear-reduce.wgsl.jinja",
"bindings": "reduce",
"dispatch": { "workgroups": "fullPartials" }
},
{
"id": "finalize",
"name": "DynamicQuantizeLinear.Finalize",
"source": { "shader": "dynamic-quantize-linear.wgsl.jinja", "inputs": { "fromPartials": true } },
"bindings": "finalizeFull",
"dispatch": { "x": 1 }
},
{
"id": "quantize",
"name": "DynamicQuantizeLinear.Quantize",
"shader": "dynamic-quantize-linear-quantize.wgsl.jinja",
"bindings": "quantize",
"dispatch": {
"threads": "ceilDiv(inputCount, tunables.ELEMENTS_PER_THREAD)",
"workgroupSize": "tunables.WORKGROUP_SIZE"
}
}
]
},
{
"id": "parallel_subgroup_reduce",
"description": "Reduces independent input blocks to min/max partials, combines them, and quantizes in a separate pass. The family uses packed reads when the input length is vec4-aligned.",
"priority": 10,
"when": ["baseContract", "inputCount > 0", "true", "parallelFullFits"],
"constants": {
"workgroupSize": "tunables.WORKGROUP_SIZE",
"elemsPerThread": "tunables.ELEMENTS_PER_THREAD",
"vec4": false,
"inputElement": "\"f32\"",
"useSubgroups": "device.features.has(\"subgroups\")"
},
"intermediates": [
{ "id": "partial_min", "dtype": "float32", "shape": "[fullPartials]" },
{ "id": "partial_max", "dtype": "float32", "shape": "[fullPartials]" }
],
"passes": [
{
"id": "reduce",
"name": "DynamicQuantizeLinear.ReduceMinMax",
"shader": "dynamic-quantize-linear-reduce.wgsl.jinja",
"bindings": "reduce",
"dispatch": { "workgroups": "fullPartials" }
},
{
"id": "finalize",
"name": "DynamicQuantizeLinear.Finalize",
"source": { "shader": "dynamic-quantize-linear.wgsl.jinja", "inputs": { "fromPartials": true } },
"bindings": "finalizeFull",
"dispatch": { "x": 1 }
},
{
"id": "quantize",
"name": "DynamicQuantizeLinear.Quantize",
"shader": "dynamic-quantize-linear-quantize.wgsl.jinja",
"bindings": "quantize",
"dispatch": {
"threads": "ceilDiv(inputCount, tunables.ELEMENTS_PER_THREAD)",
"workgroupSize": "tunables.WORKGROUP_SIZE"
}
}
]
},
{
"id": "grid_stride_reduce_vec4",
"description": "Caps the number of min/max partials and grid-strides each workgroup across the input. This bounds scratch size and finalization work for large tensors.",
"priority": 12,
"when": ["baseContract", "inputCount > 0", "inputCount % 4 == 0", "inputCount >= tunables.GRID_STRIDE_MIN_ELEMENTS", "parallelGridFits"],
"constants": {
"workgroupSize": "tunables.WORKGROUP_SIZE",
"elemsPerThread": "tunables.ELEMENTS_PER_THREAD",
"vec4": true,
"inputElement": "\"vec4<f32>\"",
"useSubgroups": "device.features.has(\"subgroups\")"
},
"intermediates": [
{ "id": "partial_min", "dtype": "float32", "shape": "[gridPartials]" },
{ "id": "partial_max", "dtype": "float32", "shape": "[gridPartials]" }
],
"passes": [
{
"id": "reduce",
"name": "DynamicQuantizeLinear.ReduceMinMax",
"source": { "shader": "dynamic-quantize-linear-reduce.wgsl.jinja", "inputs": { "gridStride": true } },
"bindings": "reduce",
"dispatch": { "x": "gridPartials" }
},
{
"id": "finalize",
"name": "DynamicQuantizeLinear.Finalize",
"source": { "shader": "dynamic-quantize-linear.wgsl.jinja", "inputs": { "fromPartials": true } },
"bindings": "finalizeGrid",
"dispatch": { "x": 1 }
},
{
"id": "quantize",
"name": "DynamicQuantizeLinear.Quantize",
"shader": "dynamic-quantize-linear-quantize.wgsl.jinja",
"bindings": "quantize",
"dispatch": {
"threads": "ceilDiv(inputCount, tunables.ELEMENTS_PER_THREAD)",
"workgroupSize": "tunables.WORKGROUP_SIZE"
}
}
]
},
{
"id": "grid_stride_reduce",
"description": "Caps the number of min/max partials and grid-strides each workgroup across the input. This bounds scratch size and finalization work for large tensors.",
"priority": 12,
"when": ["baseContract", "inputCount > 0", "inputCount % 4 != 0", "inputCount >= tunables.GRID_STRIDE_MIN_ELEMENTS", "parallelGridFits"],
"constants": {
"workgroupSize": "tunables.WORKGROUP_SIZE",
"elemsPerThread": "tunables.ELEMENTS_PER_THREAD",
"vec4": false,
"inputElement": "\"f32\"",
"useSubgroups": "device.features.has(\"subgroups\")"
},
"intermediates": [
{ "id": "partial_min", "dtype": "float32", "shape": "[gridPartials]" },
{ "id": "partial_max", "dtype": "float32", "shape": "[gridPartials]" }
],
"passes": [
{
"id": "reduce",
"name": "DynamicQuantizeLinear.ReduceMinMax",
"source": { "shader": "dynamic-quantize-linear-reduce.wgsl.jinja", "inputs": { "gridStride": true } },
"bindings": "reduce",
"dispatch": { "x": "gridPartials" }
},
{
"id": "finalize",
"name": "DynamicQuantizeLinear.Finalize",
"source": { "shader": "dynamic-quantize-linear.wgsl.jinja", "inputs": { "fromPartials": true } },
"bindings": "finalizeGrid",
"dispatch": { "x": 1 }
},
{
"id": "quantize",
"name": "DynamicQuantizeLinear.Quantize",
"shader": "dynamic-quantize-linear-quantize.wgsl.jinja",
"bindings": "quantize",
"dispatch": {
"threads": "ceilDiv(inputCount, tunables.ELEMENTS_PER_THREAD)",
"workgroupSize": "tunables.WORKGROUP_SIZE"
}
}
]
}
]
}