Anima-Turbo INT8ConvRot on M1 Max ComfyUI Runs Slower Than bf16 (47s vs 42s)
Contents
Anima-Turbo v1.0 INT8ConvRot Quantized appeared on CivitAI. It quantizes three components with a technique called INT8ConvRot: the UNet, the text encoder (qwen_3_06b_base), and the VAE (hdrVAEAnimaKrea2QWEN). The model card claims 50%+ faster inference on RTX 20 series or newer, a large VRAM reduction, and minimal visual loss.
Those acceleration claims assume a CUDA environment (INT8ConvRot acceleration on RTX 20 series or newer; RTX 10 series gets no acceleration but keeps fast inference) and say nothing about Apple Silicon / MPS. So I measured whether it loads at all in ComfyUI on an M1 Max, and what happens to speed, VRAM, and image quality. The short version: it does not even load on ComfyUI 0.24.1, the int8 matmul has no MPS kernel even after upgrading to v0.30.1, and once patched to run via dequantization it ends up slower than the unquantized bf16 checkpoint.
What this test covers
Since nearly all of my image generation goes through ComfyUI, the first step was to drop the INT8ConvRot build into that workflow and check whether it gets through model load, text encoding, 10-step sampling, and VAE decode, and how speed, VRAM, and image quality change.
Whether quantized matrix multiplication can actually execute on the Apple GPU, and whether it beats fp16, is split off into a standalone benchmark article that removes ComfyUI and measures with minimal PyTorch code, MLX, and directly written Metal kernels.
The ComfyUI API used here just sends the workflow once to the local /prompt endpoint and checks /history every second until completion. Tensors and per-layer operations are not round-tripping over HTTP; the actual inference runs in PyTorch/MPS inside the ComfyUI server process. The only thing the API changes is the measurement granularity, up to about one second.
Test environment
| Item | Details |
|---|---|
| Machine | Mac (M1 Max, 64GB RAM) |
| UI | ComfyUI 0.24.1 |
| Case A (baseline) | anima-base-v1.0.safetensors + anima-turbo-lora-v0.2.safetensors (fp16) |
| Case B (INT8ConvRot, full) | animaTurboV10Int8convrotQuantized_v10.safetensors + bundled int8 TE/VAE |
| Case C (INT8ConvRot, mixed) | int8 UNet only, existing fp16 TE/VAE (qwen_3_06b_base / qwen_image_vae) |
| Shared settings | same prompt and seed (123456789), 832×1024, steps 10, cfg 1, er_sde/simple |
Model components
| Component | Baseline (fp16) | INT8ConvRot build |
|---|---|---|
| UNet | anima-base-v1.0.safetensors + Turbo LoRA v0.2 | animaTurboV10Int8convrotQuantized_v10.safetensors (2.1GB) |
| Text Encoder | qwen_3_06b_base.safetensors | animaTurboV10Int8convrotQuantized_v10_txt.safetensors (718MB) |
| VAE | qwen_image_vae.safetensors | hdrVAEAnimaKrea2QWEN_int8.safetensors (484MB) |
It did not even load
I ran all three cases through the ComfyUI API with the same prompt and seed. Both INT8ConvRot cases, full and mixed (int8 UNet only), errored out at the load stage and never reached image generation.
| Case | Result | Time | VRAM |
|---|---|---|---|
| A: baseline (fp16 + Turbo LoRA) | Success | 58.6s | ~15.0GB |
| B: INT8ConvRot full | Failed (load error) | — | — |
| C: INT8ConvRot mixed (int8 UNet only) | Failed (load error) | — | — |
Both cases failed at the same spot.
KeyError: 'int8_tensorwise'
File "comfy/ops.py", line 1074, in _load_quantized_module
qconfig = QUANT_ALGOS[module.quant_format]
Case B raised the KeyError in CLIPLoader (text encoder load), case C in UNETLoader. The quant_format metadata embedded inside the quantized safetensors says int8_tensorwise, but that key does not exist in ComfyUI’s quantization format registry.
ComfyUI’s supported quantization formats do not include int8_tensorwise
Checking QUANT_ALGOS in comfy/quant_ops.py directly, ComfyUI 0.24.1 (code as of 2026-06-04, git commit ba9ffa0) natively supports only these four quantization formats.
| Format | Layout |
|---|---|
| float8_e4m3fn | TensorCoreFP8E4M3Layout |
| float8_e5m2 | TensorCoreFP8E5M2Layout |
| nvfp4 | TensorCoreNVFP4Layout |
| mxfp8 (only when CK is available) | TensorCoreMXFP8Layout |
All of them are NVIDIA Tensor Core FP8/FP4 formats, and a key named int8_tensorwise exists nowhere in ComfyUI core, comfy_extras, or the installed custom_nodes (grep -rl int8_tensorwise returns zero files).
_load_quantized_module only reads the quant_format metadata embedded in the state_dict being loaded and looks it up in the registry; it never checks the GPU type or backend. On the same ComfyUI 0.24.1, a CUDA machine should hit the same KeyError, so the model card’s “INT8ConvRot acceleration on RTX 20 series or newer” was not even a runnable configuration on stock ComfyUI 0.24.1, before any question of performance.
ComfyUI’s int8 support starts at v0.27.0
Reading the safetensors header directly, each quantized layer carries weight (I8) and weight_scale (F32) plus a comfy_quant sidecar tensor of U8[72]. That is a bookkeeping tensor written by ComfyUI’s own quantization scheme, not the output of a generic external quantization tool, so this model was quantized specifically for ComfyUI.
Checking the ComfyUI GitHub repository, the test machine’s v0.24.1 (commit as of 2026-06-04) was 325 commits behind the latest v0.30.1. Tracking with git log -S"int8_tensorwise", int8 quantized model support was added in Support int8 models. (#14636), first tagged in v0.27.0 (merged 2026-06-25, three weeks after the test machine’s commit). There is also a commit that names ConvRot itself: Support int8 convrot embedding lookup (#15035).
In v0.30.1’s comfy/quant_ops.py, QUANT_ALGOS has both int8_tensorwise (TensorWiseINT8Layout) and convrot_w4a4 (TensorCoreConvRotW4A4Layout) registered.
The load failure came down to the test machine’s ComfyUI predating int8 quantized model support. I vaguely remembered a version mismatch blocking an experiment once before, so this seemed like a good moment to pull my main ComfyUI forward as well.
Upgraded to v0.30.1
Between v0.24.1 and v0.30.1 there were no changes to the MPS/float8 parts of comfy/model_management.py or to Anima’s supported_inference_dtypes (I checked the diff first because a past ComfyUI update changed Qwen Image Edit’s inference dtype and pushed generation to 10 minutes, and I wanted to rule out the same kind of regression), so for those two points at least I judged the upgrade unlikely to break anything new.
I kept the local/v0.24.1 branch untouched for rollback and switched to a new local/v0.30.1 branch (the v0.30.1 tag). Updating the venv dependencies against requirements.txt bumped PyTorch from 2.10.0 to 2.13.0.
On startup it reported comfyui_version: 0.30.1, and the UNETLoader/CLIPLoader load errors were gone. Both the full and mixed cases got through loading the quantized layers.
Loading passed, but the first matmul in inference raised a different error
A new error came from the first KSampler block (adaln_modulation_self_attn in comfy/ldm/cosmos/predict2.py).
NotImplementedError: The operator 'aten::_int_mm' is not currently implemented for the MPS device.
If you want this op to be considered for addition please comment on
https://github.com/pytorch/pytorch/issues/141287 ...
As a temporary fix, you can set the environment variable
`PYTORCH_ENABLE_MPS_FALLBACK=1` to use the CPU as a fallback for this op.
Following the trace, the int8 linear layer implementation in ComfyUI’s quantization backend comfy_kitchen (int8_linear → fast_int8_mm in comfy_kitchen/backends/eager/quantization.py) calls torch._int_mm for the int8×int8 matrix multiply. That operator has no MPS kernel in PyTorch itself (PyTorch issue #141287), and both the full and mixed cases hit the same exception at the same spot. The exception also reproduced in minimal code without ComfyUI.
ComfyUI’s side (int8 support added in v0.27.0) and PyTorch’s MPS kernel coverage are separate stories; here ComfyUI had caught up and PyTorch had not.
PYTORCH_ENABLE_MPS_FALLBACK=1 was slow enough to defeat the point of Turbo
I tried the CPU fallback the error message suggests: restarted ComfyUI with PYTORCH_ENABLE_MPS_FALLBACK=1 and reran the int8 full configuration.
The baseline (fp16 + Turbo LoRA) finishes 10 steps in 49.3 seconds under these settings, but the CPU-fallback int8 full configuration had not finished KSampler’s 10 steps after 27 minutes. Every _int_mm call, per block and per step, adds MPS↔CPU tensor transfers plus the CPU-side compute, so I aborted the run.
Far from the claimed 50%+ speedup, it was tens of times slower than the plain fp16 + Turbo LoRA setup, which erases the point of Turbo (few-step fast generation).
No int8×int8 PyTorch MPS path exists
The test machine’s ComfyUI was now within one commit of origin/master (a frontend package version bump, unrelated), so this is not something that waiting for ComfyUI updates will resolve.
Looking at the package layout of comfy_kitchen, backends/ contains cuda, triton, hip (AMD ROCm), and the generic eager; an mps backend directory does not exist at all. On MPS the eager implementation is used. Its core is the fast_int8_mm above, which carries CUDA-oriented padding handling (if a.is_cuda) but no branch that considers MPS. There is a forward-looking branch on hasattr(torch, "int8_mm"), but that API does not exist yet in PyTorch 2.13.0, so it ends up calling torch._int_mm (unimplemented on MPS).
On the normal INT8ConvRot path, comfy_kitchen’s int8_linear calls torch._int_mm, and that PyTorch op has no MPS kernel. The direct cause of the stop was that in this combination, no path exists for multiplying int8 activations by int8 weights on MPS.
Adding an MPS dequantize path to comfy_kitchen
Reading comfy_kitchen/tensor/int8.py, a fallback path already existed inside the implementation for unquantized weights and certain conditions: dequantize first, then call the regular linear/mm/addmm. Only the normal path with quantized weights was wired straight to the fast path that requires torch._int_mm.
The eager implementations of dequantize_int8_simple_dtype/dequantize_int8_convrot_weight_dtype, called from TensorWiseINT8Layout.dequantize, are a simple elementwise product of int8 values and scales, with no dependence on CUDA-specific instructions. So in three places (_handle_int8_linear_tensorwise, _handle_int8_mm_tensorwise, _handle_int8_addmm_tensorwise) I added a branch that, on MPS devices only, converts the existing quantized weight (QuantizedTensor) to a plain fp16/bf16 tensor with weight.dequantize() and passes it to plain torch.nn.functional.linear (likewise for mm/addmm).
if input_tensor.device.type == "mps":
weight_fp = weight.dequantize()
return torch.nn.functional.linear(input_tensor, weight_fp, bias)
This approach never touches the fast quantized int8×int8 operation itself (torch._int_mm); it converts each layer back to fp16/bf16 once and then runs a normal matrix multiply, so it needs only operations that exist on MPS.
With the patch, all three cases generated
I restarted ComfyUI and reran the benchmark.
| Case | Result | Time | VRAM (reference only) |
|---|---|---|---|
| A: baseline (fp16 + Turbo LoRA) | Success | 46.3s | ~9.1GB |
| B: INT8ConvRot full (UNet+TE+VAE) | Success | 43.3s | measurement noise (see below) |
| C: INT8ConvRot mixed (int8 UNet only) | Success | 41.2s | ~4.0GB |
All three produced images without breakage.



B and C came out with nearly the same look (expected, since they share the same UNet). The baseline (A) differs in background and uniform colors, but that is because it compares anima-base-v1.0 + Turbo LoRA, a different setup, against Anima-Turbo, a different checkpoint; the same seed was never going to produce the same composition. For judging quantization loss, comparing B against C is the more valid pair, and I could not spot any visible breakage or degradation between those two.
The VRAM numbers came out a bit strange. ComfyUI unloads the previous model when switching cases, so case B’s “usage” turned negative (free memory increased). Model load/unload timing is not aligned across cases, so this rough measurement cannot compare true peak memory.
Speed was 43.3s for B and 41.2s for C against the 46.3s baseline, slightly faster but close to noise. Nothing here supports the model card’s claimed 50%+ speedup.
Fixing the mismatched baseline
The baseline (A) so far was anima-base-v1.0 + Turbo LoRA v0.2, a stand-in setup from the period when the Anima-Turbo checkpoint itself was unreleased. It is a different lineage from the original “Anima-Turbo v1.0” checkpoint that the INT8ConvRot build quantized, so the comparison had not isolated quantization as the only variable.
The official Anima model page on CivitAI (circlestone_labs, modelId 2458426) has an unquantized anima-turbo-v1.0 (anima_turboV10.safetensors, bf16, 3.99GB), so I fetched it and redid the comparison within the same Anima-Turbo lineage. Turbo is baked into this checkpoint; no separate LoRA is needed.
| Case | UNet | Notes | Time |
|---|---|---|---|
| Official Anima-Turbo v1.0 (unquantized bf16) | anima_turboV10.safetensors | Runs natively, no patch needed | 42.2s |
| INT8ConvRot full | animaTurboV10Int8convrotQuantized_v10.safetensors + int8 TE/VAE | Does not run without the MPS dequantize patch above | 47.2s |
| INT8ConvRot mixed (int8 UNet only) | Same + fp16 TE/VAE | Same | 45.2s |
The unquantized official build was faster than both patched INT8ConvRot variants. The dequantize patch adds per-layer work (int8 → apply scale → convert to fp16), so it is slower than just using the pre-quantization weights directly. On a Mac, quantization does not speed anything up; the extra conversion cost makes it slower.


On quality, being the same checkpoint lineage, the composition, hairstyle, and outfit matched almost exactly. The color shift is not limited to the eyes: the int8 build’s irises lean red-purple with redness along the eye rims, and the blues in the background and collar are stronger. But there was no structural breakage or degradation, which lines up with the model card’s “minimal visual loss” claim.
Slower than the official unquantized build and equal in quality, so the only benefit left in running the INT8ConvRot build on a Mac through this dequantize patch is the smaller download (2.1GB vs 3.99GB). What a different MPS path can do is separated out into the standalone benchmark article.
What the dequantize patch does and does not show
This patch never uses torch._int_mm (the fast int8×int8 matmul that MPS lacks); it dequantizes each layer to fp16 and runs a normal matrix multiply, so the runtime computation is effectively fp16. The speedup from quantized tensor math that INT8ConvRot actually sells was not tested here.
The model weights themselves are not broken and work correctly once dequantized. Generation itself does work on a Mac (MPS) through this path, but perceived speed is about the same as the fp16 setup, and the quantization speedup could not be reproduced this way. The smaller download (2.1GB body, 718MB TE, 484MB VAE) remains a genuine disk-space benefit, but that is all it offers, which makes it hard to call useful.
What runs and what does not
Breaking down how far int8 quantized tensor math gets on a Mac, stage by stage:
| Stage | Result |
|---|---|
| Loading quantized tensors | Works (ComfyUI v0.27.0+ supports int8_tensorwise) |
torch._int_mm on CPU | Works (confirmed with minimal code in the test machine’s ComfyUI venv) |
torch._int_mm on MPS | Fails (aten::_int_mm is not currently implemented for the MPS device) |
PYTORCH_ENABLE_MPS_FALLBACK=1 | Runs, but as the warning says, it is a CPU fallback, not int8 acceleration on the Mac GPU (MPS) |
This patch (MPS-only weight.dequantize() in comfy_kitchen/tensor/int8.py, escaping to fp linear/mm/addmm) | Runs, but this is not an int8×int8 speed test either |
To state it precisely: in the current ComfyUI + comfy_kitchen + PyTorch MPS stack, there is no path that executes INT8ConvRot’s int8×int8 GEMM on MPS. The CPU fallback and the dequantize route both run, but neither serves as a test of int8 acceleration on the Mac GPU.
For what it is worth, PyTorch’s MPS op coverage is still being tracked (PyTorch issue #141287), and Apple officially documents that custom Metal kernels for MPS can be written. So the “unimplemented” here is about the normal path of this software stack, not about a Mac being physically incapable, I think.
The design is CUDA-first to begin with. ComfyUI’s int8_tensorwise documentation states the supported-GPU requirement as SM 7.5 or higher. That is an NVIDIA Compute Capability generation, and the model card’s “acceleration on RTX 20 series or newer” mirrors that design directly. Apple Silicon’s MPS was never in scope.
According to NVIDIA’s GPU Performance Background User’s Guide, the Turing generation can execute INT8 math on Tensor Cores or CUDA cores. The original implementation assumes int8 matrix multiplies optimized for that hardware. A basic Metal kernel like the one in this test shares the input types but not the executed instructions or the accumulated optimization behind them.
It is technically understandable, then, that this runs faster than fp16 on NVIDIA hardware. But whether the model card’s 50%+ speedup number is right or wrong cannot be judged from these M1 Max results. About all this test can say is that the fast path that works for CUDA does not exist as-is on MPS.
Direct measurements of quantized matrix multiplication from PyTorch, Metal, MPSMatrix, and MLX continue in the INT8ConvRot standalone benchmark on M1 Max.