Repository navigation
fix(kernel): resolve batch stride overwrite and multi-turn context crashes for BitNet-2.4B on ARM64 - #642
fix(kernel): resolve batch stride overwrite and multi-turn context crashes for BitNet-2.4B on ARM64#642Abhishek Khanra (IshekKhal) wants to merge 4 commits into
Conversation
…M64 BitNet-2.4B inference
|
@microsoft-github-policy-service agree |
…multi-turn context stability
|
Thanks for working on this. The SiLU → ReLU² change matches what I measured in #588 / #639. I tested this PR on Linux aarch64 (Neoverse-N1: ARMv8.2 + dotprod, no i8mm), clang 18, with the official
Sampled chat output ( Steps: I haven't tested x86 or Apple Silicon. Happy to rerun on this machine if you push an update. Analysis assisted by Claude Code; runs were done on my machine and numbers checked by me. |
…; garbage with its patch) Assisted-by: Claude Code (Anthropic, claude-opus-5-5)
… patches - Integrate Scott Guyton's ARM NEON dot-product kernel and block-interleaved scalar fallback in quants.c to eliminate weight permutation on ARM64 - Update top-level CMakeLists.txt to automatically detect and apply the engine patch to 3rdparty/llama.cpp during build configuration - Fix GGML_SOURCES_BITNET in src/CMakeLists.txt to include both MAD and LUT source files instead of overwriting the variable
|
Matheus, thank you for the detailed test run and for linking your work on Here is what was happening across the points you flagged, and what has been pushed to this branch: 1. Default Build & Submodule Patch ApplicationYou were spot on about the build pipeline. Having Fix:
2. 51M Perplexity & ARM64 Interleaved Weight LayoutYour perplexity measurement (51,732,149 on In the GGUF Fix:
3. Tokenizer BPE Merge InferenceGood catch on the token shift (325k down to 289k). Inferring Fix:
4. Verification on Linux aarch64The updated branch ( To test on your Neoverse-N1 machine: git fetch origin pull/642/head && git checkout -B test-pr642 FETCH_HEAD
cmake -B build -DBUILD_SHARED_LIBS=OFF -DLLAMA_BUILD_COMMON=ON -DLLAMA_BUILD_TOOLS=ON \
-DCMAKE_C_COMPILER=clang-18 -DCMAKE_CXX_COMPILER=clang++-18
cmake --build build --target llama-completion -j $(nproc)
./build/bin/llama-completion -m ggml-model-i2_s.gguf -no-cnv \
-p "The capital of France is" -n 24 --temp 0 -t 2 -c 512Expected output under greedy decoding ( If you have a chance to rerun perplexity on |
|
Thanks for the quick turnaround. I reran the updated branch ( With your steps (
With the default A static build links fine, so it's easy to miss. The same recipe with only Not tested: x86-64 and Apple Silicon. Analysis assisted by Claude Code; runs were done on my machine and numbers checked by me. |
…library linker collision
|
Thank you for the thorough review and empirical verification on ARM64 Linux (Neoverse-N1). It's great to see the perplexity alignment (18.8444 ± 1.30426 matching the HF 18.79 baseline) and the verified token counts confirming parity with upstream. Regarding the shared library build failure with default I've pushed a commit (4cdf35a) that removes the redundant inclusion from On a related note: as a heads-up, this will be my final patch submitted to this repository. The Really appreciate your thorough review, benchmarking, and feedback throughout this PR! |
|
Thanks for the update. I reran the third version (
Analysis assisted by Claude Code; runs were done on my machines and numbers checked by me. |

Summary
This pull request resolves critical memory corruption, block size misalignments, model architecture discrepancies, and ARM64 kernel weight-interleaving bugs that prevent correct inference on ARM64 (Apple Silicon, Neoverse-N1) and AVX2 with Microsoft's official 2.4B BitNet ternary model (
microsoft/bitnet-b1.58-2B-4T-gguf).Prior to this fix, running the official 2.4B ternary model on Apple Silicon or Linux ARM64 produced endless repetition of token 31 (
@ @ @) or unrecoverable garbage output with extreme perplexity (~51.7M), as reported by community members in #588, #602, and #639.With these patches applied, the 2.4B model achieves articulate, grammatically fluent output under greedy decoding (
--temp 0), achieving 18.84 perplexity on wiki.test.raw (matching the Hugging Face 18.79 baseline), and speeds of 83.3 tokens/sec (prompt processing) and 49.3 tokens/sec (token generation) on Apple Silicon CPU with zero GPU offloading.Technical Root Cause Analysis
1. Batch Output Stride Overwrite in MAD Kernels (
src/ggml-bitnet-mad.cpp)ggml_vec_dot_i2_i8_s_1x1andggml_vec_dot_i2_i8_s_1xNkernels, the output array was indexed ass[row]ands[row + rb]. Becausebs(batch stride) was ignored in the write-back stage, every evaluated batch row wrote to indexrowrather thanrow * bs, immediately clobbering preceding output vectors in memory.s[(row + rb) * bs].2. Block Size Layout Mismatch & Interleaved Weight Layout on ARM64 (
ggml-cpu-i2s.c&quants.c)@ @ @repetition or high perplexity (51.7M onwiki.test.raw) under greedy decoding.ggml-cpu-i2s.cdefined#define QK_I2_S 64when__ARM_NEONwas active. However, the GGUF ternary quantization format standardizes on 128 elements per block (QK_I2_S 128). This 64 vs 128 discrepancy shifted pointer arithmetic across all weight blocks by 50%.I2_Sformat, weights are stored block-interleaved: 32 packed weight bytes match against 128 activations, where 2-bit fieldquants.cunpacked bytes sequentially (i / 4, bit shift6 - 2*(i % 4)), pairing every weight with the wrong activation and yielding a systematically permuted matrix multiplication.#define QK_I2_S 128uniformly across all paths. Integrated Scott Guyton's ARM NEON dot-product kernel (vdotq_s32) and 32-stride block-interleaved scalar fallback intoquants.c(fromisHuangXin/llama.cpp#2), properly accumulating 16-byte halves against 32-stride activation vectors.3. FFN Activation Function Mismatch (
src/models/bitnet.cpp)models/bitnet.cppinstantiated FFN gate activation asLLM_FFN_SILU. Microsoft's 2.4B ternary checkpoint was trained using squared ReLU (LLM_FFN_RELU_SQR). Using SiLU completely altered the activation dynamic range.LLM_FFN_RELU_SQR.4. Submodule Integration & Automatic CMake Patch Application (
CMakeLists.txt&src/CMakeLists.txt)cmake -B buildorpython setup_env.pyproduced unchanged binaries becausepatches/0001-fix-arm64-2b-inference.patchwas not applied to3rdparty/llama.cpp, andsrc/ggml-bitnet-mad.cppwas omitted from build targets.src/CMakeLists.txt,set(GGML_SOURCES_BITNET ggml-bitnet-lut.cpp)accidentally overwroteggml-bitnet-mad.cppon the preceding line without exporting to parent scope. Furthermore,3rdparty/llama.cppis a submodule that required manual patching.CMakeLists.txtto rungit apply --checkduring configuration. If unapplied, CMake automatically appliespatches/0001-fix-arm64-2b-inference.patchto3rdparty/llama.cpp.src/CMakeLists.txtsoGGML_SOURCES_BITNETdefines bothggml-bitnet-mad.cppandggml-bitnet-lut.cpp.ggml-bitnet-mad.cppto3rdparty/llama.cpp/ggml/src/ggml-cpu/CMakeLists.txtto compile kernel objects directly intolibggml-cpu.a.5. Multi-Token Prompt Column Stride & Activation Parallelism (
ggml-cpu-i2s.c&ggml-cpu.c)ggml_gemm_i2_i8_s,vec_dotwalked weight rows against a single activation vector writing contiguously, but activation-parallel calls needed one result per activation column at thebs-strided slot.ggml-cpu-i2s.cto invokeggml_vec_dot_i2_i8_sper column withnrc=1, placing outputs ats_col + (r0 + r) * bs. Restored batched GEMM fast paths inggml-cpu.c.6. BLAS Multi-Turn Context Crash on Context >= 32 Tokens (
ggml-blas.cpp&ggml.c)signal: segmentation fault (EXC_BAD_ACCESS).ggml_backend_blas_device_supports_opclaimedMUL_MATwheneverbatch >= 32andto_float != NULL. BLAS then calleddequantize_row_i2_swith an unaligned 4x byte stride offset (x + i01 * nb01), jumping past the buffer into unmapped memory. Additionally,ggml_row_size()returnedneinstead ofne / 4forI2_Stensors, tripping tensor view bounds assertions.src0->type != GGML_TYPE_I2_Stoggml-blas.cppso ternary matrix operations remain strictly on the dedicated CPU SIMD kernels, and updatedggml_row_size()inggml.cto return(size_t)ne / 4forI2_SandTL1types.Changes Summary
CMakeLists.txtpatches/0001-fix-arm64-2b-inference.patchto3rdparty/llama.cppusinggit apply --checkduring CMake configuration.src/CMakeLists.txtGGML_SOURCES_BITNETto include bothggml-bitnet-mad.cppandggml-bitnet-lut.cpp.src/ggml-bitnet-mad.cpps[row]tos[(row + rb) * bs]across1x1,1x4, and1xNkernels for AVX2 and ARM NEON.patches/0001-fix-arm64-2b-inference.patchQK_I2_S 128, integrated Scott Guyton's ARM NEON dot-product kernel (vdotq_s32) and interleaved scalar fallback inquants.c, corrected FFN activation toLLM_FFN_RELU_SQRinmodels/bitnet.cpp, addedggml-bitnet-mad.cpptoggml-cpu/CMakeLists.txt, fixed column strides inggml-cpu-i2s.c, excludedI2_Sfrom BLAS, and alignedggml_row_sizetone / 4.Empirical Verification & Benchmarks
Test Environment
arm64)microsoft/bitnet-b1.58-2B-4T-gguf(1.13 GB,i2_squantization)-ngl 0,-t 8,--temp 0)Benchmark Metrics
--temp 0)@ @ @or gibberish)wiki.test.raw, 8 x 512)Reproduction Steps
Check out this branch:
Configure and build (CMake will automatically detect and apply the engine patch to
3rdparty/llama.cpp):Run inference on the official 2.4B checkpoint with greedy decoding:
./build/bin/llama-cli -m bitnet-b1.58-2B-4T-i2_s.gguf -p "The capital of France is" -n 24 --temp 0 -ngl 0 -t 8Expected output: