Make fixnum packing configurable and disable ints#9379
Conversation
Disable for release of JRuby 10.1.0.0 to avoid polymorphism. Work after release will make all four widths configurable and reduce the complexity of this logic. See jruby#9379
With or without Lilliput, byte and int fixnums do not pack any tighter than short or long fixnums, so disable both. Add config properties to control the use of all three.
The following situations have purpose-built factories, with all others falling back on generic versions that check configs repeatedly: * Long only, with cache * Long only, without cache * Long and short, with cache * Long and short, without cache Of these, the fastest performance for tight numeric loops still seems to be "long only. An analysis of the resulting assembly with AI help confirms my suspicion that widening and narrowing short values repeatedly ends up costing enough cycles to offset any gains from having smaller objects for those numeric ranges. We may want to consider having the compact fixnums be opt-in and not enabled by default (with "long only with cache" being the default mode).
|
This is complete, but after some extensive benchmarking on MacOS AArch64 the new ShortFixnum may introduce more instruction overhead than the size is worth. The bimorphic nature of having both LongFixnum and ShortFixnum does not appear to impact things much, but the ShortFixnum constantly widening and narrowing its values for reads and initialization of new instances adds enough overhead to a small loop to reduce performance. It also appears to interfere with escape analysis, both on HotSpot and Graal JIT. Some loop benchmarks follow. All shorts: def foo
i = 0
while i < 32_000
i+=1
j = 0
while j < 32_000
j+=1
end
end
end
10.times {
t = Time.now
foo
puts Time.now - t
}Long and short with caching, HotSpot 25: Long only with caching, HotSpot 25 ( Long only without caching, HotSpot 25 ( Same on HotSpot 26 (escape analysis seems to have improved? Enabling cache degrades this back to 25 speeds.): Same on GraalVM 25 (escape analysis eliminating most objects): On the other hand, an array of 1M short-ranged values goes from 37,391,992 bytes to 29,391,992 bytes, almost 22% less memory. That savings only applies to short-ranged integers but it still makes this a tough call since most loops will also be in short range. |
During benchmarking I realized two things about the split fixnums:
I created this patch to allow enabling and disabling all four widths of fixnum for experimentation, but having only short and long enabled by default.
I this is a WIP and I don't like how it conditionally enables the different widths. The code is far too complex. A better option would be to have fully specialized paths for each combination (possibly generated) and choose one at boot that will never again check a condition.
will land a separate patch for 10.1.0.0 that just hard disables IntFixnum to keep it minimal.