Files
ThereforeGames 722dadcbc5 Rebrand, add INT4 support & quantization modes
Rebrand the project as "ComfyUI Quantization Toolkit" and introduce native INT4 (ConvRot W4A4) support, mixed INT4/INT8 modes, and expanded quantization policies. Add an INT4 torch.compile compatibility shim, INT4-aware lazy-compile changes, richer LoRA stack nodes with autogrow, and a Quantized LoRA entry/apply pair. Replace ad-hoc prints with structured logging, bump package metadata to 2.0.0, and add documentation (advanced usage, checkpoints, policies) plus many unit tests for compile, lazy-compile, LoRA, adapter caching, saving, and modes. Misc: kernel config, model save/load, and numerous runtime/adapter refactors and robustness fixes.
2026-08-04 07:11:56 -04:00

168 lines
6.3 KiB
Python

import logging
import comfy.patcher_extension
_KERNEL_CONFIG_WRAPPER_KEY = "int8_kernel_config_sync"
def _extract_transformer_options(args, kwargs):
transformer_options = kwargs.get("transformer_options", None)
if transformer_options is None and len(args) > 5:
transformer_options = args[5]
if transformer_options is None:
transformer_options = {}
return transformer_options
def _kernel_config_sync_wrapper(executor, *args, **kwargs):
transformer_options = _extract_transformer_options(args, kwargs)
config = transformer_options.get("int8_triton_kernel_config", None)
if isinstance(config, dict):
try:
from . import int8_fused_kernel
int8_fused_kernel.set_fixed_kernel_config(config, source="transformer_options", silent=True)
except Exception:
pass
return executor(*args, **kwargs)
def _ensure_kernel_config_wrapper(model_patcher):
model_patcher.remove_wrappers_with_key(
comfy.patcher_extension.WrappersMP.APPLY_MODEL,
_KERNEL_CONFIG_WRAPPER_KEY,
)
model_patcher.add_wrapper_with_key(
comfy.patcher_extension.WrappersMP.APPLY_MODEL,
_KERNEL_CONFIG_WRAPPER_KEY,
_kernel_config_sync_wrapper,
)
def _to_kernel_config(block_m, block_n, block_k, group_size_m, num_warps, num_stages):
return {
"BLOCK_M": int(block_m),
"BLOCK_N": int(block_n),
"BLOCK_K": int(block_k),
"GROUP_SIZE_M": int(group_size_m),
"num_warps": int(num_warps),
"num_stages": int(num_stages),
}
class INT8KernelConfigTuner:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"model": ("MODEL", {"tooltip": "INT8 model whose Triton kernel settings should be synchronized during sampling."}),
"run_microbench": ("BOOLEAN", {"default": False, "tooltip": "Benchmark candidate kernel settings now and use the fastest result for this model."}),
"block_m": ("INT", {"default": 128, "min": 16, "max": 512, "step": 16, "tooltip": "Triton BLOCK_M tile size for fixed INT8 matmul kernels."}),
"block_n": ("INT", {"default": 128, "min": 16, "max": 512, "step": 16, "tooltip": "Triton BLOCK_N tile size for fixed INT8 matmul kernels."}),
"block_k": ("INT", {"default": 64, "min": 16, "max": 512, "step": 16, "tooltip": "Triton BLOCK_K reduction tile size for fixed INT8 matmul kernels."}),
"group_size_m": ("INT", {"default": 8, "min": 1, "max": 64, "step": 1, "tooltip": "Triton GROUP_SIZE_M launch grouping value for fixed INT8 matmul kernels."}),
"num_warps": ("INT", {"default": 4, "min": 1, "max": 16, "step": 1, "tooltip": "Number of Triton warps per program for fixed INT8 matmul kernels."}),
"num_stages": ("INT", {"default": 4, "min": 1, "max": 8, "step": 1, "tooltip": "Number of Triton pipeline stages for fixed INT8 matmul kernels."}),
"bench_m": ("INT", {"default": 2048, "min": 64, "max": 16384, "step": 64, "tooltip": "M dimension used by the optional synthetic kernel microbenchmark."}),
"bench_k": ("INT", {"default": 4096, "min": 64, "max": 16384, "step": 64, "tooltip": "K dimension used by the optional synthetic kernel microbenchmark."}),
"bench_n": ("INT", {"default": 4096, "min": 64, "max": 16384, "step": 64, "tooltip": "N dimension used by the optional synthetic kernel microbenchmark."}),
"bench_warmup": ("INT", {"default": 2, "min": 1, "max": 20, "step": 1, "tooltip": "Warmup iterations before timing each candidate kernel config."}),
"bench_iterations": ("INT", {"default": 6, "min": 2, "max": 100, "step": 1, "tooltip": "Timed iterations per candidate kernel config."}),
"bench_include_scalar": ("BOOLEAN", {"default": False, "tooltip": "Include scalar-weight kernel candidates in the benchmark. Usually leave off for per-row INT8 models."}),
}
}
RETURN_TYPES = ("MODEL",)
FUNCTION = "apply_kernel_config"
CATEGORY = "loaders"
DESCRIPTION = "Apply INT8 Triton kernel config. Optionally run an on-demand microbench and log recommended env vars."
def apply_kernel_config(
self,
model,
run_microbench,
block_m,
block_n,
block_k,
group_size_m,
num_warps,
num_stages,
bench_m,
bench_k,
bench_n,
bench_warmup,
bench_iterations,
bench_include_scalar,
):
model_patcher = model.clone()
manual_config = _to_kernel_config(
block_m,
block_n,
block_k,
group_size_m,
num_warps,
num_stages,
)
try:
from . import int8_fused_kernel
except Exception as e:
logging.warning(f"INT8 Kernel Config: Triton kernel module unavailable ({e}).")
return (model_patcher,)
if not int8_fused_kernel.is_fixed_kernel_mode():
logging.warning("INT8 Kernel Config: INT8_TRITON_AUTOTUNE=1 is enabled; fixed kernel config values will be ignored.")
selected_config = manual_config
if run_microbench:
try:
logging.info(
f"Quantization Toolkit INT8: running kernel microbench "
f"(M={bench_m}, K={bench_k}, N={bench_n}, warmup={bench_warmup}, iters={bench_iterations})..."
)
best_config, results = int8_fused_kernel.microbench_fixed_kernel_configs(
m=bench_m,
k=bench_k,
n=bench_n,
warmup=bench_warmup,
iterations=bench_iterations,
include_scalar=bench_include_scalar,
extra_candidates=[manual_config],
)
selected_config = best_config
logging.info("Quantization Toolkit INT8: microbench results (top 3):")
for row in results[:3]:
logging.info(f"Quantization Toolkit INT8: avg_ms={row['avg_ms']:.3f} config={row['config']}")
except Exception as e:
logging.warning(f"INT8 Kernel Config: microbench failed ({e}); using manual config.")
applied_config = int8_fused_kernel.set_fixed_kernel_config(
selected_config,
source="INT8KernelConfigTuner",
)
if "transformer_options" not in model_patcher.model_options:
model_patcher.model_options["transformer_options"] = {}
else:
model_patcher.model_options["transformer_options"] = model_patcher.model_options["transformer_options"].copy()
opts = model_patcher.model_options["transformer_options"]
opts["int8_triton_kernel_config"] = dict(applied_config)
_ensure_kernel_config_wrapper(model_patcher)
logging.info("Quantization Toolkit INT8: recommended environment variables for persistent config:")
for line in int8_fused_kernel.format_kernel_config_env_lines(applied_config):
logging.info(f"Quantization Toolkit INT8: {line}")
return (model_patcher,)
NODE_CLASS_MAPPINGS = {
"INT8KernelConfigTuner": INT8KernelConfigTuner,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"INT8KernelConfigTuner": "INT8 Kernel Config",
}