Replacing a Rust Enum with a 64-Bit Word Made My Interpreter 17% Faster
Posted by metrofun 3 days ago
Comments
Comment by fpoling 1 hour ago
One cannot expect a compiler to come up with such encoding.
Comment by pbiggar 21 minutes ago
Comment by trickypr 11 minutes ago
1. Do you really want the rust compiler to run at the speed of an llm?
2. Compiler optimisations are already extremely unpredictable with deterministic compilers[1], I hate to think how unpredictable your compiler would be.
3. What if someone else wants to build the software, do they have to decide on optimisations now? What if the optimisation depends on your features not available on old generations of CPU? (There is a reason we don’t compile with -march=native)
4. Compilers already have “unsafe” optimisations, but people rarely enable them (-ffast-math)
Comment by pbiggar 5 minutes ago
Like just open Claude Code and ask it to find optimizations. That's the right place for this kind of optimization.
Comment by win311fwg 1 hour ago
Comment by dymk 36 minutes ago
Comment by dzaima 22 minutes ago
(also; if anything, the title is implying the exact opposite of "Rust compiler was able to optimize ...", "Replacing a Rust [...] with [...]" is clearly moving away from Rust-magic to something else)
Comment by lowbloodsugar 4 hours ago
Comment by krick 2 hours ago
Comment by maplant 1 hour ago
enum Value {
Float(f64),
Ptr(*const T),
}
Do you want the compiler to disallow certain bit patterns in the Float variant simply so that it can implement NanBoxing?Comment by vlovich123 1 hour ago
That being said, the optimization is complex that may be insufficient:
> For my boxing scheme, I picked a bias value such that the lowest two bits end up being 10. That 1 in bit index 1 indicates that doubles can't be directly compared for equality. Amazingly, we only lose two bits of exponent, and we keep the full precision of the mantissa, meaning we lose no significant digits in the flonum representation.
This suggests the optimization needs more information about specifically how you want to box the float. There probably is some primitives worth considering standardizing to make this kind of optimization possible so that the tunable parameters are passed as const generic values.
Comment by speedstyle 30 minutes ago
Comment by gigatexal 4 hours ago
Comment by compiler-guy 3 hours ago
This new code also supplies similar abstractions. That actual specific code is much harder to reason about, but most users--and even the next person who works on the interpreter--simply won't care, or even know what is going on underneath the hood. The abstractions provided by the author do that work and apparently do it cleanly.
For most use-cases, that extra hand-written code isn't worth it. But in specific cases it can be, and the author has actually measured the value and determined that it is.
Comment by steveklabnik 3 hours ago
Also, Rust does try to do some of these optimizations itself. These aren't exposed in the stable language to let you do some more advanced things, but it wouldn't be impossible for you to get the best of both worlds by letting you communicate this stuff more directly to the compiler. Right now those things are more like "this value is where you should put the tag" than the more advanced stuff here, though. Would be cool to see someday!
Comment by lowbloodsugar 3 hours ago
Comment by tialaramex 2 hours ago
Rust provides for example NonZeroU8 which is an 8-bit unsigned integer that's never zero, leaving it with 255 possible values and a convenient niche. You cannot make one of these yourself directly, because the mechanism used by Rust itself is a deliberately perma-unstable compiler-only proc macro which says "Hey compiler, I promise I only ever use bit patterns 0x01 through 0xFF inclusive".
Today you can either - hide a NonZero type inside your type and use that to get the niche, or, use an enum itself which automatically knows ever pattern it didn't use is a niche. In the future a hypothetical "Pattern Types" feature would let you make such types yourself as easily as Rust does
Personally I would like to make a Balanced set of types, like BalanacedI8 (the 8-bit integers except the most negative, so -127 to +127 inclusive) because I think lots of people have a use for types like i8 or i32 but don't need their unbalanaced most-negative value and could re-purpose it this way. And you can make such types... indeed I have... but it's only really practical in unstable Rust.
Comment by fpoling 1 hour ago
For a system language I wish Rust would support such things rather than coming with NonZero hacks.
Comment by steveklabnik 1 hour ago
NonZero isn't a hack: it's an example of a common pattern. If pattern types were available today, you'd still want NonZero, as an example of a pretty standard pattern.
The idea is, as always: prove out the specific version, then generalize.
Comment by tom_ 4 hours ago
The bit operations involved are pretty simple and won't take you long to figure out even if you've never done them before.
Comment by diath 3 hours ago
Comment by dzaima 2 hours ago
In the case of a tagged object in Rust, depending on how well the compiler can wrangle through it, you might even be able to add a `.unpack()` method that returns a pretty enum from a packed value, that you can pattern-match on or whatever, and let the compiler remove all the code of unpacking unused cases.
(using that directly for the addition example would end up less efficient of course, but still most likely beneficial. It's after this when there's a potential true readability vs performance tradeoff)
Comment by locknitpicker 3 hours ago
...unless it's supported by the language as a first class feature. See for example C++ and RVO.
Comment by diath 3 hours ago
Comment by mwkaufma 3 hours ago
Comment by nwhitehead 20 minutes ago