Thanks to visit codestin.com
Credit goes to Github.com

Skip to content

Conversation

@headius
Copy link
Member

@headius headius commented Nov 20, 2025

This adds specialized RubyFixnum subtypes for byte, short, int, and long, making RubyFixnum abstract. All allocations of a Fixnum are boxed using the most compact size.

This drastically reduces the memory requirements for non-64-bit fixnums and likely improves allocation rate due to the smaller object and reduced overhead from zeroing.

@headius headius force-pushed the small_fixnums branch 2 times, most recently from 1cf15df to 5cc39e8 Compare November 21, 2025 05:03
@headius headius changed the base branch from master to 10.1-dev December 2, 2025 15:46
This adds specialized RubyFixnum subtypes for byte, short, int, and
long, making RubyFixnum abstract. All allocations of a Fixnum are
boxed using the most compact size.

This drastically reduces the memory requirements for non-64-bit
fixnums and likely improves allocation rate due to the smaller
object and reduced overhead from zeroing.
The fixnum specialization triggered a bug in hash calculation
where something like [257,1] could hash differently depending on
when during execution it was calculated. The specific case was in
resolv.rb, which sets up a hash of integer pairs pointing at the
Class object associated with that DNS code. Later accesses hashed
the given pair differently and ended up replacing the earlier
entry.

The source of this problem is not entirely clear to me, but it was
fixed by ensuring that we never construct a cacheable fixnum value
and always use the cached values. I suspect this is a flaw in the
design of the safeRecurse logic, which uses an IdentityHashMap
when invoking `hash` on nested elements, but I cannot explain how
having two different fixnum instances could trigger this problem.

There are other issues with safeRecurse that, when fixed, would
also work around this problem (see jruby#9086) but it is at
least clear that the fixnum specialization must be "absolute"; in
other words, we must always use the cached instances for the
supported range (to ensure they retain identity) and always use the
same fixnum type for a given value range (so we don't end up with
two fixnums of the same value but different class).

These problems do not affect CRuby because fixnums are idempotent
due to their use of tagged pointers; there can never be two fixnum
objects because they are never objects.
There's no particular value in checking the exact type here, so
just confirm that it's a RubyFixnum-adjacent type.
The change here is based on two ideas:

* Manually-inlined range checks for each level in each factory
  method is error-prone and hard to update (e.g. if we change how
  fixnum caching works)

It also fixes an inability to set the fixnum cache size larger
than short range or smaller than byte range by restoring the cache
range checks to both integer and byte paths. This may be reexamined
for jruby#9087.
Current object layout has 1 byte of waste for ByteFixnum, not
really saving us anything by having another polymorphic type. We
can revisit this in the future.
@headius headius merged commit e7a065d into jruby:10.1-dev Dec 2, 2025
76 checks passed
@headius headius deleted the small_fixnums branch December 2, 2025 17:03
@headius headius added this to the JRuby 10.1.0.0 milestone Dec 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant