Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to instance variable shaping #7516

Open
wants to merge 2 commits into
base: 9.5-dev
Choose a base branch
from

Conversation

headius
Copy link
Member

@headius headius commented Dec 8, 2022

This PR will incorporate a few improvements to how we "right-size" user-defined objects to pack instance variables.

  • Better parent-driven layout of fields, so they can be shared across subclasses.
  • Persistent shapes for stale objects; after we realize it needs to grow, existing objects can continue living with the smaller shape.
  • Improved caching to avoid polymorphic thrashing when the fields are laid out the same.
  • Analysis of implementation and exploration of other improvements.

Stretch goals might include playing with field-doubling to store numeric values without wrappers, but the primary goal is to do a better job of choosing the right shape or evolving into the right shape over time.

@headius
Copy link
Member Author

headius commented Dec 8, 2022

So far this is safe to do in 9.4, but we'll see how it goes.

@headius headius added this to the JRuby 9.4.1.0 milestone Dec 8, 2022
@headius headius modified the milestones: JRuby 9.4.1.0, JRuby 9.4.2.0 Feb 6, 2023
@headius headius modified the milestones: JRuby 9.4.2.0, JRuby 9.4.3.0 Feb 28, 2023
@headius headius marked this pull request as ready for review May 23, 2023 14:58
@headius headius modified the milestones: JRuby 9.4.3.0, JRuby 9.4.4.0 May 23, 2023
@headius headius modified the milestones: JRuby 9.4.4.0, JRuby 9.5.0.0 Oct 9, 2023
@headius headius mentioned this pull request Jan 22, 2024
19 tasks
We acquire a list of likely instance variables by walking the
target class and all parent classes looking for instance variable
instructions in the methods in the method table. Previously, this
logic would add variables from the bottom up, checking the current
class's methods first and then the superclass and so on. This had
the effect of creating new layouts for the shared instance vars
rather than storing vars from parent classes in the same slots for
all child classes.

The new logic use a recursive call to add variables starting with
the top class and progressing down the hierarchy. Variable names
are first inserted into a sorted TreeSet at east level, and then
any new variables go into a LinkedHashSet to ensure parent
variables are laaid out before child variables. This ensures that
all child classes will use the same slots for instance variables
originally accessed in a parent class.

This does not currently improve caching of variable accessors, but
it may in the future. If we cache accessors based on the class
they originate from, we can cache parent accessors in all children
without having to do more guarding than we do at present.

Example of multi-class hierarchy preserving layout:

class A; def initialize; @foo = 1; @baz = 1; end; end; class B < A; def initialize; @bar = 1; @foo = 2; end; end; class C < A; def initialize; @quux = 1; @baz = 2; end; end; A.new; B.new; C.ne

Printing out class and found variables:

A
[@baz, @foo]
B
[@baz, @foo, @bar]
C
[@baz, @foo, @quux]

Note that in all three instances, the @baz and @foo variables
occupy the same first two slots. As long as the hierarchy has not
changed, we can rely on this layout in child classes.
@headius headius changed the base branch from master to 9.5-dev January 22, 2024 16:28
In order to evolve the shape of objects as more instance variables
are discovered, we need to be able to access  multiple different
shaped instances with their own layouts. Previously, only one
shape could be associated with a given class, based on a static
inspection of all instance variable accesses in the class's
hierarchy. In order to keep stale-shaped objects functional, this
commit adds a shape reference to all shaped RubyObject subtypes.
With this we can allocate a first object using no instance vars
(falling back on the default varTable layout) and as instance vars
are encountered modify allocation to create wider object shapes.

The step here simply adds the shape reference to all shaped
objects; evolving that shape and updating the allocator will come
in future commits.
@headius
Copy link
Member Author

headius commented Jan 22, 2024

Rebased to 9.5-dev.

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.

None yet

1 participant