[fix](kt-kernel): AVX-VNNI-256 RAWINT4 per-expert weight loading#2092
Open
blazingphoenix7 wants to merge 2 commits into
Open
[fix](kt-kernel): AVX-VNNI-256 RAWINT4 per-expert weight loading#2092blazingphoenix7 wants to merge 2 commits into
blazingphoenix7 wants to merge 2 commits into
Conversation
… backend NativeMoEWrapper always passes weights as per-expert pointers (gate_projs), but TP_MOE<AVXVNNI256_RAW_INT4_MOE_TP>::load_weights only accepted flat buffers and threw, so native RAWINT4 checkpoints could not load at all on hosts where this backend is selected. The throw escapes on the CPUInfer worker thread and kills the process during startup. Gather the TP slices from the per-expert tensors into the same per-partition staging buffers the flat path builds, then reuse the unchanged per-part loader. Slicing math mirrors the per-expert branch of the K2 K-Group loader: contiguous row block per partition for gate/up, per-row column gather for down. Works for any threadpool count; flat mode is untouched.
New per-commit CPU test comparing forward outputs of backend instances that hold the same weight bytes loaded through different paths: per-expert vs flat (AVX-VNNI-256 at one and two subpools, AVX2 at one), permuted vs identity physical_to_logical_map, and int32 pack-quantized vs uint8 byte-packed storage. Identical kernels on identical bytes must agree bitwise, so these assert torch.equal; the one vs two subpool comparison of the same flat weights uses a 5e-3 mean relative tolerance because the split down projection sums partials in a different order. The AVX-VNNI-256 per-expert cases are the regression tests for the preceding fix; without it they fail with the load exception.
1 task
Contributor
There was a problem hiding this comment.
Code Review
This pull request adds support for loading weights via per-expert pointers in the AVX-VNNI RAWINT4 MoE operator, alongside a comprehensive Python test suite to verify load-path equivalence across different configurations. Feedback suggests adding defensive validation to ensure all per-expert pointer vectors are properly populated and contain non-null pointers to prevent potential segmentation faults.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Native RAWINT4 checkpoints (the Kimi K2 layout) cannot load on CPUs where the AVX-VNNI-256 backend is selected.
NativeMoEWrapperalways hands the C++ side per-expert weight pointers (moe_config.gate_projs = [[...]], one pointer per expert), but the backend's TP wrapper only accepted flat buffers:The wrapper never sets the flat pointers, so on a build without AMX support running on a CPU with avx_vnni, where
_select_rawint4_backendprefers this backend for group sizes up to 256, weight loading always throws. The exception is raised on the CPUInfer worker thread, which has no handler, so the process dies during startup. This is the consumer-CPU lane that #1897 asked about.The fix teaches
TP_MOE<AVXVNNI256_RAW_INT4_MOE_TP>::load_weightsto gather TP slices from per-expert pointers into the same per-partition staging buffers the flat path already builds, then hand off to the unchanged per-part loader. The slicing arithmetic is the same as the per-expert branch of the K2 K-Group loader ink2-moe.hpp, which serves the AMX/AVX512 builds today: gate/up take a contiguous row block per partition, down takes a per-row column gather. Because the gather works for any partition count, RAWINT4 serving with--kt-threadpool-countgreater than 1 also works on these hosts now. The flat path is untouched, and the transient staging cost is identical to flat mode and to the K2 loader.Found by equivalence testing the RAWINT4 load paths against each other (same weight bytes loaded two ways must produce bitwise identical forwards) while looking into the reports in #2076.
Changes
kt-kernel/operators/avx2/rawint4_avxvnni-moe.hpp: per-expert pointer support in the TP wrapper'sload_weights, mirroring the K2 loader. Flat mode unchanged.kt-kernel/test/per_commit/test_moe_rawint4_load_equivalence.py: new CPU per-commit test, registered withregister_cpu_ci. Each check runs two backend instances holding the same weight bytes loaded through different paths and compares full forward outputs withtorch.equal, since identical kernels on identical bytes must agree bitwise:physical_to_logical_mapthrough per-expert load, on both backendsThis PR is correctness and coverage only; no performance behavior changes and none is claimed.
Not addressed here, left as known limitations of the AVX2 sibling:
AVX2RawInt4_MOEstill requires--kt-threadpool-count 1in per-expert mode because it serves weights zero-copy from the source tensors and a direct pointer cannot express the down projection's column slice. Lifting that needs a strided weight view or a copy fallback, a separate change. Hosts with avx_vnni stop hitting that limit with this fix since selection prefers the VNNI backend there.write_weights_to_bufferon the VNNI backend (GPU expert staging) also remains unimplemented, as before.Testing
On an Intel Core Ultra 5 225 (AVX2 + AVX-VNNI, no AVX512, no AMX), Linux, gcc 15,
CPUINFER_CPU_INSTRUCT=NATIVECPUINFER_ENABLE_AMX=OFF:test_moe_rawint4_load_equivalence.py: all six tests run (this host has avx_vnni, so nothing skips), PASSED.test_moe_rawint4_accuracy.py: both backends at qlen 1 and 16, three iterations each, every diff under its threshold (about 0.003 for AVX2, about 0.014 for AVX-VNNI against the torch reference), PASSED.test/per_commiton this host:test_basic_cpu,test_load_experts_count_guard,test_port_checker,test_moe_avx2_accuracy_bf16,test_moe_avx2_accuracy_fp8,test_moe_gptq_int4_accuracyand the placeholders pass; the sglang adapter tests pass under pytest (12 passed, 5 integration cases skip without model files). The eighttest_moe_amx_*files stop at torch calls that require CUDA before reaching any kt-kernel code (CPU-only torch here); they cannot run on this machine with or without this change.Rebuilding with the fix reverted, the first per-expert AVX-VNNI check aborts the process with the exception quoted above escaping on the CPUInfer worker thread, while the flat-mode loads in the same run keep working. That is the exact failure the serving path hits today, and it shows the flat path is not affected by this change.
Before submitting