Skip to content

Force a Thread's block to optimize eagerly#8576

Merged
headius merged 1 commit intojruby:10-devfrom
headius:aot_thread_block
Jan 21, 2025
Merged

Force a Thread's block to optimize eagerly#8576
headius merged 1 commit intojruby:10-devfrom
headius:aot_thread_block

Conversation

@headius
Copy link
Member

@headius headius commented Jan 17, 2025

The block of code passed to Thread.new is frequently only executed once during a program's runtime, and in such cases there's often heavy lifting performed within the block such as an IO or Queue loop. Because we do not support any form of on-stack replacement, these blocks may never fully optimize and will remain in whatever execution mode JRuby starts them in (usually the slowest "startup" interpreter).

This patch adds logic from Thread.new into the block body to force completion of builds steps that would optimize the body. This is akin to the behavior of the command-line target script, which we also eagerly compile due to the great number of single-script utilities and benchmarks in the Ruby ecosystem.

This patch also plumbs that force-ability through the other "buildable" execution units: methods and block-based methods like define_method. This may become useful in the future to have more direct control over when a target block or method optimizes.

The block of code passed to Thread.new is frequently only executed
once during a program's runtime, and in such cases there's often
heavy lifting performed within the block such as an IO or Queue
loop. Because we do not support any form of on-stack replacement,
these blocks may never fully optimize and will remain in whatever
execution mode JRuby starts them in (usually the slowest "startup"
interpreter).

This patch adds logic from Thread.new into the block body to force
completion of builds steps that would optimize the body. This is
akin to the behavior of the command-line target script, which we
also eagerly compile due to the great number of single-script
utilities and benchmarks in the Ruby ecosystem.

This patch also plumbs that force-ability through the other
"buildable" execution units: methods and block-based methods like
define_method. This may become useful in the future to have more
direct control over when a target block or method optimizes.
@headius headius added this to the JRuby 10.0.0.0 milestone Jan 17, 2025
@headius
Copy link
Member Author

headius commented Jan 17, 2025

Performance improvement based on the first attempt here:

COMMAND

$ jruby -Xjit.logging -e 'loop { th = eval("def x; Thread.new { sleep; i = 0; while i < 10_000_000; i+=1; end }; end; x"); sleep 1; t = Time.now; th.wakeup; th.join; puts Time.now - t }'

BEFORE (note no JIT output except the failure to compile the -e script)

[] jruby 
2025-01-17T13:43:42.664-06:00 [main] ERROR Ruby : failed to compile target script: -e - AOT not supported for scripts containing eval.
0.721603
0.665112
0.6637540000000001
0.649576
0.663361
0.6614
0.650284
0.64456
0.660865
0.6648860000000001

AFTER (note JIT output and significant improvement in the performance of each thread's execution)

2025-01-17T13:44:20.562-06:00 [main] ERROR Ruby : failed to compile target script: -e - AOT not supported for scripts containing eval.
2025-01-17T13:44:20.695-06:00 [main] INFO JITCompiler : block done jitting: Object new &||1 at (eval at -e:1):0
0.19823400000000002
2025-01-17T13:44:21.925-06:00 [main] INFO JITCompiler : block done jitting: Object new &||1 at (eval at -e:1):0
0.15478
2025-01-17T13:44:23.089-06:00 [main] INFO JITCompiler : block done jitting: Object new &||1 at (eval at -e:1):0
0.153646
2025-01-17T13:44:24.256-06:00 [main] INFO JITCompiler : block done jitting: Object new &||1 at (eval at -e:1):0
0.080538
2025-01-17T13:44:25.346-06:00 [main] INFO JITCompiler : block done jitting: Object new &||1 at (eval at -e:1):0
0.07915900000000001
2025-01-17T13:44:26.432-06:00 [main] INFO JITCompiler : block done jitting: Object new &||1 at (eval at -e:1):0
0.07181399999999999
2025-01-17T13:44:27.510-06:00 [main] INFO JITCompiler : block done jitting: Object new &||1 at (eval at -e:1):0
0.062474

@headius
Copy link
Member Author

headius commented Jan 17, 2025

Note idea came out of #8406, where a one-shot, long-running background thread services all timeouts. If that thread's code immediately optimized, it may be able to keep up with the onslaught of tiny timeout jobs.

@headius
Copy link
Member Author

headius commented Jan 21, 2025

I experimented with this optimization and the timeout issue in #8406, and it does help, but the many client threads still swamp the one timeout thread. See ruby/timeout#58 for the ongoing issue.

@headius headius merged commit a653604 into jruby:10-dev Jan 21, 2025
54 of 73 checks passed
@headius headius deleted the aot_thread_block branch January 21, 2025 18:32
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