BitNet: The Repo That Tries to Make a CPU Think in Ternary

Microsoft’s `bitnet.cpp` turns 1.58-bit models into a new inference stack, where packed weights, custom kernels, and cache-aware math matter more than floating-point precision.

9 min read • View on GitHub • More from microsoft

A workshop-like CPU socket filled with tiny ternary tiles, gears, and memory blocks. The image explains BitNet’s core bet: inference can be reorganized so ordinary hardware does less floating-point work and more packed low-bit routing.
BitNet’s promise is not just smaller weights. It is a different machine for moving numbers around.
Key Takeaways

The CPU That Should Not Work This Well

BitNet starts with a provocation: a CPU should not be able to do this kind of inference, at least not if you think in the usual FP16 or BF16 terms. Yet the repo is built around the idea that if you make the math cheap enough, ordinary hardware stops looking ordinary.

The surprise is not just that the models are small. It is that `bitnet.cpp` turns ternary weights into a full inference path, so the machine behaves less like a general-purpose floating-point engine and more like a purpose-built low-bit appliance.

bitnet.cpp can run a 100B BitNet b1.58 model on a single CPU, achieving speeds comparable to human reading (5-7 tokens per second), significantly enhancing the potential for running LLMs on local devices.

What 1.58 Bits Actually Means

BitNet b1.58 uses ternary weights, with values in {-1, 0, 1}. That is where the 1.58-bit claim comes from, but the implementation still has to live in real hardware, which means the values are packed into 2-bit representations such as `I2_S`.

uint8_t packed = (uint8_t)((q0 << 6) | (q1 << 4) | (q2 << 2) | (q3 << 0));

That line is the practical trick. Four 2-bit values fit into a single byte, and once the model is represented that way, the rest of the stack can be optimized around predictable packing, decoding, and accumulation behavior.

BitNet is not one kernel. It is a dispatch system that chooses the cheapest valid route for the hardware in front of it.

DimensionBitNet b1.58Typical FP16 / BF16 inference
Weight representationTernary values packed into low-bit formatsDense floating-point weights
Main bottleneckMemory movement and layoutRaw arithmetic throughput
Primary execution styleSpecialized low-bit kernelsGeneral matrix multiplication
Hardware fitBest when kernels match the target CPU or GPUBroadly portable across modern accelerators
Operational complexityHigher, because dispatch and presets matterLower, because the path is more uniform
Main benefitMuch lighter inference on commodity hardwarePredictable numerical behavior and generality
Main constraintSpecialization and tuning debtHigher compute and memory cost

Two Ways to Avoid Matrix Multiplies

The repo’s center of gravity lives in two C++ kernel paths: `src/ggml-bitnet-lut.cpp` and `src/ggml-bitnet-mad.cpp`. Both intercept matrix multiplication through the `ggml` backend, but they do it differently.

The LUT path uses precomputed outcomes for low-bit interactions. The MAD path keeps the multiply-add structure, but reshapes it around packed signed 2-bit values and SIMD-friendly execution. Same goal, different tradeoff.

// Conceptual dispatch shape
if (ggml_bitnet_can_mul_mat(op, hw)) {
    return use_bitnet_kernel(op, hw);
}
return use_standard_mm(op);

That dispatch layer matters more than it sounds like. It is the point where BitNet stops being a model format and becomes an execution policy.

A split close-up of a matrix multiply. One side shows dense floating-point tiles crashing together in a blur, while the other side shows packed ternary tokens being routed through a lookup table and then through block-aligned SIMD lanes. The image explains how BitNet replaces generic multiplication with specialized low-bit execution paths.
The technical pivot is not just smaller numbers. It is choosing the right low-bit route before the multiply ever happens.

Why the Cache Matters More Than the Model Size

`include/gemm-config.h` is where the project gives away its real obsession. It defines block sizes differently for AVX and NEON, which tells you BitNet is not simply compressing weights. It is fitting work to the memory hierarchy.

That is the quiet truth behind the speed claims. If your tiling matches L1 and L2 behavior, then tiny weights become more than a storage win. They become a way to keep the CPU fed without wasting cycles on cache misses.

#if defined(__ARM_NEON)
#define ROW_BLOCK_SIZE  ...
#define COL_BLOCK_SIZE  ...
#elif defined(__AVX__)
#define ROW_BLOCK_SIZE  ...
#define COL_BLOCK_SIZE  ...
#endif

The GPU Path Is Still Bitwise, Not Magical

The CUDA side does not turn BitNet into a generic GPU story. It still has to decode packed low-bit data, use `lop3.b32` bitwise operations, and then hand work off to tensor-core-style machinery.

That is important because it keeps the repo honest. The acceleration comes from careful representation and instruction selection, not from a mysterious GPU spell that erases the cost of the underlying math.

The Orchestrator Is Part of the Product

`setup_env.py` is the unglamorous file that makes the rest of this believable. It handles downloading, converting, quantizing, and preparing the right build for the target machine, which means the repository is not just kernels. It is a workflow.

That workflow matters because BitNet’s performance depends on the whole chain. A ternary model that is not converted correctly, quantized correctly, or compiled for the right instruction set is just an idea with bad ergonomics.

BitNet vs the Usual Inference Stack

BitNet is easiest to understand in contrast. Conventional FP16 or BF16 deployment is broad and familiar. General quantization stacks like llama.cpp are also broad, but they stay closer to the standard matrix-multiply world. BitNet goes further and changes the execution model itself.

StackStrengthWeakness
FP16 / BF16 deploymentPortable and numerically straightforwardHeavy on compute and memory
General quantized inferenceGood tradeoff between size and compatibilityStill mostly dense-math shaped
BitNetAggressive low-bit execution tuned for specific hardware pathsMore specialized and harder to generalize

The tradeoff is obvious once you see it. BitNet is not trying to be the least surprising way to run an LLM. It is trying to be the most efficient way to run one on the hardware that is already there.

What This Repo Proves, and What It Still Doesn’t

BitNet proves that ternary inference can be made real, fast, and surprisingly practical. It also proves that this kind of performance does not come for free. The repo leans on hardcoded presets, architecture-specific tuning, and a fairly narrow slice of supported hardware behavior.

So the honest read is not that GPUs are obsolete. It is that the frontier has shifted. In some cases, the smartest thing you can do is stop asking the hardware to behave like a floating-point supercomputer and instead ask it to behave like a very good low-bit machine.