mirror of
https://github.com/leejet/stable-diffusion.cpp.git
synced 2026-11-11 03:24:30 +00:00
feat: support packed INT4 convrot models (#2276)
Co-authored-by: noctrex <noctrex@gmail.com>
This commit is contained in:
1 parent
58bb3c1b1c
commit
7952693004
48 files changed
+2003
-102
No files matched your search
@@ -104,6 +104,16 @@ and backend `graph_plan_*` interfaces do not dispatch these extensions.
|
||||
|
||||
## Backend services
|
||||
|
||||
Packed ComfyUI INT4 linear layers use `sd.linear_w4_convrot.v1`, built by
|
||||
`ggml_mul_mat_w4_convrot`. The operator consumes byte-packed I8 weights and the
|
||||
explicit output of `sd.quantize_i8_convrot.v1`; F32 callers get a quantization
|
||||
node during graph construction. Logical output dimensions are stored in the
|
||||
operator attributes, so backend copies of the packed activation do not need to
|
||||
retain the producer's op marker. W4A4 uses row scales; W4A8 additionally exposes
|
||||
the codebook, channel scales, and FP8 relative scales as tensor dependencies.
|
||||
CPU, CUDA/HIP/MUSA, and Vulkan register kernels through the same extension API
|
||||
as INT8. No ggml submodule changes are required.
|
||||
|
||||
Services are discovered through `ggml_backend_reg_get_proc_address`:
|
||||
|
||||
| Service | Purpose |
|
||||
|
||||
+40
-1
@@ -100,7 +100,46 @@ The packed runtime activation tensor contains the I8 activation rows and their f
|
||||
|
||||
LoRA adapters are applied at runtime without modifying the INT8 weights. The INT8 convrot path computes the base linear output, while LoRA, LoHa, LoKr, and raw weight-difference adapters compute their output corrections from the original, unrotated activation and add them to the base output. `--lora-apply-mode auto` selects this path for models containing INT8 tensorwise weights. If `immediately` is requested, sd.cpp falls back to runtime application because merging an adapter would require dequantizing and rotating its weight update, then recalculating the per-row scales and requantizing the result.
|
||||
|
||||
The shared CUDA/HIP/MUSA convrot activation paths currently require a group size of `256`; other supported group sizes use CPU execution.
|
||||
The shared CUDA/HIP/MUSA convrot activation paths support H64 and H256. Vulkan also supports H64 activation quantization; its INT8 matrix multiplication path currently requires H256.
|
||||
|
||||
## Packed INT4 convrot
|
||||
|
||||
ComfyUI `convrot_w4a4` and `asym_w4a8_int8` safetensors load directly, including
|
||||
checkpoints that mix packed INT4, INT8 tensorwise, and floating-point layers.
|
||||
Packed weights retain their checkpoint byte layout; they are not expanded or
|
||||
converted to GGUF Q4 blocks at load time.
|
||||
|
||||
- `convrot_w4a4`: signed 4-bit weights, low nibble first, with F32 per-output
|
||||
`weight_scale`. Activations use the same INT8 convrot quantization as above;
|
||||
the format name does not select a native INT4 activation kernel.
|
||||
- `asym_w4a8_int8`: 4-bit indices into `weight_codebook` (16 F32 values), F32
|
||||
`weight_s_channel`, and F8_E4M3 `weight_s_rel` per 16 input features. The
|
||||
metadata must specify `group_size: 16`. CPU evaluates the floating-point
|
||||
codebook; GPU kernels requantize it to INT8 with `amax / 127`, introducing a
|
||||
small additional rounding error.
|
||||
|
||||
Both formats imply convrot rotation even without a `convrot` flag; missing
|
||||
`convrot_groupsize` defaults to 256.
|
||||
|
||||
Both formats reuse the convrot activation cache when the input scale is `1` and
|
||||
produce F32 outputs with optional bias. CPU provides a portable implementation.
|
||||
CUDA/HIP/MUSA use shared packed-weight kernels without requiring INT8 BLAS;
|
||||
CUDA and supported AMD targets use packed byte-dot instructions, with portable
|
||||
byte arithmetic for other targets. Vulkan requires integer dot product support
|
||||
and at least 13312 bytes of shared memory for W4A4 or 16452 bytes for W4A8.
|
||||
GPU W4 kernels require the logical input width to be divisible by 64; H64 and
|
||||
H256 are supported. Other shapes use CPU fallback. HIP W4A8 selects wave64 WMMA
|
||||
by the active compilation target's
|
||||
intrinsic support, preferring the GFX12 fragment layout over the GFX11 layout.
|
||||
There is no GPU architecture allowlist: newer targets supporting either
|
||||
intrinsic automatically use its matching layout. Selection happens per device
|
||||
image, including multi-architecture builds; targets supporting neither intrinsic
|
||||
retain the integer-dot fallback. W4A4 retains integer dot products on HIP.
|
||||
|
||||
LoRA application remains at runtime, using the original unrotated activations
|
||||
and logical weight dimensions. Packed weights and their codebook/scale tensors
|
||||
are excluded from weight type conversion. `SD_USE_UPSTREAM_GGML` builds reject
|
||||
these formats because they require the application extension API.
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -60,6 +60,8 @@ static UNetConfig detect_from_weights(const String2TensorStorage& tensor_storage
|
||||
```
|
||||
|
||||
Use `TensorStorage` metadata, especially `n_dims` and `ne`, to infer shapes.
|
||||
Use `logical_ne0()` for Linear input widths; packed INT4 weights store half of
|
||||
the logical width in `ne[0]`. Keep raw `ne` values for storage layout calculations.
|
||||
Do not load or parse tensor data for config detection.
|
||||
|
||||
Detection should respect `prefix`. For nested weights, construct full names from
|
||||
|
||||
Reference in new issue
Block a user