8 ms·
Wouldn't this strategy still allow performance gains when running on a processor that does NaN propagation?
by mckeed 4y ago
Wouldn't this strategy still allow performance gains when running on a processor that does NaN propagation?
- zamadatix 4y agoI think you'd also want to check the NaN route is actually faster in practice on the CPUs you are targeting but beyond that as long as you're willing to maintain the dedicated codepath for it (or are fine with saying it only runs on specific targets) then you should be good.
- jcranmer 4y agoYou'd have to be sure that the compiler will also preserve NaN propagation through optimization, which is itself not a safe assumption (internal models for compiler IR often model floating-point as producing a nondeterministic choice of NaN representation, which means that it's not illegal for an optimization to fail to propagate NaNs). As for performance gains, well, the thing is you're not likely to see much of a gain. The post argues that "standard is to store the floats in the high 32 bits of the pointer, with the low 32 bits as a tag", except I've never seen a system like this (tbh, I haven't really done too many VM systems, so my experience isn't great). That tagging model seems like they're assuming a pointer-tagging system [0], rather than true NaN-boxing. With NaN-boxing, you'd want to use the high bits of the payload, so that the low 32-bits can be used directly for whatever value is boxed. See https://searchfox.org/mozilla-central/source/js/public/Value.h#554-557 https://searchfox.org/mozilla-central/source/js/public/Value... for an example of NaN-boxing in Firefox's JS engine [1]. What you save from somebody already using high payload bits from the tag is... an or instruction at the end to set the bits. Realistically, though, there's very little need for a VM to support 32-bit float types. They're not actually faster than 64-bit floats (except for division) in hardware, and the main benefit of smaller float types is more compact memory representation--except if you're boxing all the values anyways, the 32-bit float types also aren't any smaller. [0] Classical pointer tagging uses low bits for the tag, because you rely on pointer alignment. However, with 64-bit systems generally having only 48-bit pointers (57-bit with x86's 5-level page table, but this requires processes to opt in to using 5-level page tables anyways), it makes a lot of sense on 64-bit systems to also use high bits for the tag these days instead. [1] JS doesn't have 32-bit floats, but it does have 32-bit integers, so you can at least get an idea for how the payload would be set if the boxed value could represent 32-bit floats.
- bjourne 4y agoIt really depends on how you are boxing. Assuming a four byte object header a boxed float32 consumes 8 bytes. A boxed float64 consumes 12 bytes but! Is quite likely to require 4 bytes of padding to align it to an 8 byte boundary. It adds up quickly if you have many floats.