← Tue, Sep 8 digest

Replacing a Rust Enum with a 64-Bit Word Made My Interpreter 17% Faster

read article ↗ hn thread ↗

summary

Replacing a 16-byte Rust tagged enum with a 64-bit tagged word representation significantly improves interpreter performance by reducing memory traffic and allowing values to fit into CPU registers. This approach leverages low-bit tagging to store integers, floats, and pointers within a single word, minimizing the overhead of memory alignment and stack spilling. Despite the need for occasional heap boxing for overflow values, the gains in cache efficiency and streamlined machine code outweigh the costs. Ultimately, this change results in a consistent speedup across various benchmarks, including floating-point heavy tasks.

key points

  1. Low-bit tagging enables the storage of 62-bit integers, floating-point values, and pointers in a 64-bit word by stealing unused bits from memory addresses and integer ranges. This reduces the memory footprint of each value, leading to better cache utilization and fewer memory accesses during execution.
  2. Replacing the Rust enum with a u64 newtype prevents the compiler from generating inefficient code characterized by excessive stack spilling and disjoint basic blocks. The resulting machine code is more linear and compact, allowing the CPU to handle value dispatch and arithmetic operations more efficiently.
  3. The implementation of a self-tagging scheme for floating-point numbers allows most doubles to be stored directly in the tagged word without losing precision. While this requires more instructions to unbox and re-box than simple integers, it is still faster than the previous enum-based approach due to the reduction in memory operations.

who should read it

VM engineers, language designers, and Rust developers interested in low-level memory optimization and interpreter performance.

discussion positive

consensus

Commenters agree that the performance gains achieved by replacing a Rust enum with a manually packed 64-bit word are the result of a specific architectural choice rather than a failure of the Rust compiler. While manual bit manipulation reduces readability and maintainability, it is considered an acceptable trade-off for critical hot paths in an interpreter. There is general agreement that while the compiler cannot automatically perform such complex semantic changes, the language could benefit from more expressive 'pattern types' to allow developers to specify encoding schemas safely.

top insight

The most valuable insight is the discussion on 'pattern types' and 'niches,' suggesting that Rust could allow developers to define their own non-enum types with user-defined niches (like NonZeroU8). This would enable the creation of highly optimized, memory-efficient types without resorting to raw unsafe bit manipulation, effectively bridging the gap between high-level abstractions and low-level machine efficiency.

Generated by an LLM from the article and thread. Check the sources before relying on it.