Skip to content

[fix] avoid walking constructor instructions on every call#7762

Merged
kares merged 4 commits intojruby:jruby-9.3from
kares:fix-java-super-slowness_9.3
Aug 8, 2023
Merged

[fix] avoid walking constructor instructions on every call#7762
kares merged 4 commits intojruby:jruby-9.3from
kares:fix-java-super-slowness_9.3

Conversation

@kares
Copy link
Member

@kares kares commented Apr 20, 2023

Since JRuby 9.3 Ruby sub-classes of Java classes are backed with a generated Java class,
the interpreter context for the constructor was always rebuild from scratch on every IR method call -> effectively looping through method's instructions.


NOTE: while this improves numbers by 5x it's still 4x slower than JRuby 9.2 (should probably implement a way of JIT-ing and avoid method lookup), the script from: #7763 ends up with the following numbers:

$ jruby slow_java_super.rb 
jruby 9.2.20.0 (2.5.8) 2021-11-02 1a3255440b OpenJDK 64-Bit Server VM 11.0.14.1+1 on 11.0.14.1+1 +jit [linux-x86_64]

1 RubyList.new(...) elapsed real-time: 3.4137503600068158
2 RubyList.new elapsed real-time: 2.6153695250031888
$ jruby slow_java_super.rb 
jruby 9.3.10.0 (2.6.8) 2023-02-01 107b2e6697 OpenJDK 64-Bit Server VM 11.0.14.1+1 on 11.0.14.1+1 +jit [x86_64-linux]

1 RubyList.new(...) elapsed real-time: 54.96447179099778
2 RubyList.new elapsed real-time: 54.98580553600186
bin/jruby slow_java_super.rb 
jruby 9.3.11.0-SNAPSHOT (2.6.8) 2023-04-20 0a07e21a3b OpenJDK 64-Bit Server VM 25.322-b06 on 1.8.0_322-b06 +jit [x86_64-linux]

1 RubyList.new(...) elapsed real-time: 9.032593938994978
2 RubyList.new elapsed real-time: 7.724980847997358

@kares kares linked an issue Apr 20, 2023 that may be closed by this pull request
Copy link
Contributor

@ikaronen-relex ikaronen-relex left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, except that I do think interpreterContextForJavaConstructor should be volatile. (I also don't like the massive code duplication, but at least you haven't made it worse.)

@enebo
Copy link
Member

enebo commented May 12, 2023

@kares not related to this PR but I wonder if adding a single instr to IR which can call Java super would be a longer term solution? The tradeoff would be that all constructors would be immediately JIT compiled but we already are leaning on ASM here so I do not see much of a difference. It would remove having the split interpreter. This is a weirder suggestion than it may appear since IR JIT makes jruby methods but it probably can be repurposed to emit a valid Java constructor.

The other option is to make current JIT capable of compiling the two halves but this is more difficult. temp vars in IR are java locals in JIT so it would require spilling locals into an array? to then pass back in for the second half (or guarantee no temps are accessible across the split). I did attempt to make splitable methods a few years ago so we could split on BBs on huge methods but it was invasive (it did not spill locals but changed all local access to indirect through Object[] temp array). That experiment only ran into large refactoring of JVMViisitor. Conceptually it is a path to synthetic methods which this split logic fits.

@kares
Copy link
Member Author

kares commented May 12, 2023

The other option is to make current JIT capable of compiling the two halves but this is more difficult.

I still do not understand all of the code but indeed approaching a way to JIT from an IR method seemed a bit impossible (without a massive refactoring of the Java super bits).

Haven't thought about having an instruction for the Java super, sounds weird but at least the JITing would be much simpler (as you said byte-code is already generated in first pass anyways so shoudn't matter). Guess, we will have to attempt something on 9.4 at least. Basically anything to make the performance reasonable - comparable to 9.2.

@enebo
Copy link
Member

enebo commented May 12, 2023

Haven't thought about having an instruction for the Java super, sounds weird but at least the JITing would be much simpler (as you said byte-code is already generated in first pass anyways so shoudn't matter). Guess, we will have to attempt something on 9.4 at least. Basically anything to make the performance reasonable - comparable to 9.2.

I have considered using Java static call instr in IR so that we can eliminate a few instrs since they end up being a single Java call (or can be). Super is a little weirder but is in the same neighborhood. Temp vars are already all Object so you can see some of the new instrs to support pattern matching already are returning Integer and not Fixnum.

@kares kares force-pushed the fix-java-super-slowness_9.3 branch from bef9cce to 2118c23 Compare May 15, 2023 17:05
kares added 3 commits May 15, 2023 19:05
Ruby sub-classes of Java classes are reified with a generated Java class

The interpreter context for the constructor was always rebuild from
scratch on every IR method call -> effectively looping through method's
instructions.
@kares kares force-pushed the fix-java-super-slowness_9.3 branch from 2118c23 to dae4cbe Compare May 15, 2023 17:05
@enebo enebo added this to the JRuby 9.3.11.0 milestone May 15, 2023
@kares
Copy link
Member Author

kares commented Jun 8, 2023

moving this out of draft - have some more work locally to clean things up and at least remove duplication but to keep things minimal for 9.3 this at least avoids the contention issue on Java super calls.

@kares kares marked this pull request as ready for review June 8, 2023 06:21
@kares kares requested a review from headius June 28, 2023 11:12
Copy link
Member

@headius headius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple naming things but otherwise looks good!

@kares kares merged commit cc1011a into jruby:jruby-9.3 Aug 8, 2023
@kares kares deleted the fix-java-super-slowness_9.3 branch August 8, 2023 10:30
@kares
Copy link
Member Author

kares commented Aug 14, 2023

this is now merged and expected to be in 9.3.11.0 and 9.4.4.0

the numbers are still not the same as in 9.2 but the contention issue is handled, for the record here are the numbers for 9.4 (using the script from #7763):

jruby 9.4.3.0 (3.1.4) 2023-06-07 3086960792 OpenJDK 64-Bit Server VM 11.0.14.1+1 on 11.0.14.1+1 +jit [x86_64-linux]

100x RubyList.new elapsed real-time: 40.68082665700058
100x RubyList2.new elapsed real-time: 5.673665171998437
100x ArrayList.new elapsed real-time: 0.16680395399453118

200x RubyList.new elapsed real-time: 88.13843810799881
200x RubyList2.new elapsed real-time: 12.741242688003695
200x ArrayList.new elapsed real-time: 0.18367314200440887

jruby 9.4.4.0-SNAPSHOT (3.1.4) 2023-08-14 cf956f1e53 OpenJDK 64-Bit Server VM 11.0.14.1+1 on 11.0.14.1+1 +jit [x86_64-linux]

100x RubyList.new elapsed real-time: 2.806557794989203
100x RubyList2.new elapsed real-time: 6.87243668200972
100x ArrayList.new elapsed real-time: 0.18653793400153518

200x RubyList.new elapsed real-time: 4.38154152900097
200x RubyList2.new elapsed real-time: 13.899855226991349
200x ArrayList.new elapsed real-time: 0.19196245599596296

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.

Java subclass (instantiation) performance degraded in 9.3

4 participants