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

Skip to content

YJIT: Add compilation log #11818

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

Merged
merged 16 commits into from
Oct 17, 2024
Merged

YJIT: Add compilation log #11818

merged 16 commits into from
Oct 17, 2024

Conversation

nirvdrum
Copy link
Contributor

@nirvdrum nirvdrum commented Oct 7, 2024

This PR introduces a new compilation log to help gain insights into how YJIT is optimizing an application. Finding the correct amount information to display for a given context is a bit hard. Some folks may want an entire log while others are only interested in the most recent events. This PR tries to strike a balance by offering a few different operating modes.

Command-Line Flag

This PR introduces the --yjit-log flag, which functions like a hybrid of --yjit-stats and --yjit-dump-disasm. The presence of an optional argument will dictate where the compilation log is written.

No Argument

--yjit-log without an argument will print the compilation log to $stderr when the application exits. Events are buffered until the application exit in order to avoid mixing messages up with other output from the application. To constrain the total amount of memory used, a circular buffer is used to store the logged events. Only the entries present in the buffer at the time the application exits will be printed.

--yjit-compilation-log without an argument will print the compilation log to $stderr as events occur. It will be logically equivalent to running with --yjit-compilation-log=/dev/stderr, just without file truncation. Events will still be collected in the internal circular buffer for programmatic access.

Quiet Argument

--yjit-log=quiet will activate the log but not print or write it anywhere. The log contents will be accessible in code via RubyVM::YJIT.log. A circular buffer is used to store the log entries to constrain growth. Consequently, only the last N=1024 entries are available. Reading the log will remove all stored values, freeing up space for new entries.

Directory Argument

The --yjit-log flag can take an optional argument pointing at a directory (e.g., --yjit-log=$PWD). In this case, it functions very similarly to --yjit-dump-disasm: a new file named yjit_$PID.log will be created in the specified directory and all compilation events will be logged to that file. In this way, one directory can capture output from multiple processes. NB: the directory must exist when starting Ruby, otherwise the argument will be treated as if it were a file name (see below).

Unlike the case where output is sent to $stderr, all events will be written to the file as they occur. The circular buffer will still be populated in the event you wish to work with the log in memory (accessible via RubyVM::YJIT.compilation_log).

File Argument

The --yjit-log flag can take an optional argument pointing at a file (e.g., --yjit-log=my_compilation.log). This works very similarly to the directory argument case, except instead of a file name being generated, it will use the one supplied.

The file will be created when the process starts. If the file already exists, it will be truncated. This supports a nice workflow whereby you can re-run the same ruby invocation repeatedly and avoid mixing results for multiple runs. Moreover, since the file is truncated rather than re-created, you can tail -f my_compilation.log without having to restart the tail command each time you re-run ruby.

Programmatic Control

In addition to the command-line flag, you can enable or disable the compilation log via RubyVM::YJIT.enable and the compilation_log keyword argument. It is designed to function similarly to the programmatic control of YJIT's runtime stats. Principally, it accepts three values:

  • false: Don't enable the compilation log (more of a placeholder since you can't call this method again to disable)
  • true: Enable the compilation log and print it to $stderr at exit
  • :quiet: Enable the compilation log, but do not write or print it

NB: There is not an option to enable the log and write to a file. If you want to operate in that mode you should supply the --yjit-log argument.

Programmatic Access

Check if Enabled

You can check if the YJIT compilation log is currently enabled with RubyVM::YJIT.log_enabled?.

Reading the Log

The last N=1024 compilation log entries are accessible via RubyVM::YJIT.log. The output has the type Array[Tuple[Time, String]], providing the log is enabled. If it is not, the value will be nil to help differentiate from a log where no compilation has occurred yet (i.e., an empty array) . The log is automatically cleared when accessed so subsequent reads will be guaranteed to contain new events.

@matzbot matzbot requested a review from a team October 7, 2024 18:51
@nirvdrum nirvdrum force-pushed the yjit-compilation-log branch from dabc7bc to 5e14be1 Compare October 8, 2024 19:12
@nobu
Copy link
Member

nobu commented Oct 9, 2024

In the commit log, at the beginning of "Directory Argument" and "File Argument",

The --yjit-compilation-log falg can take an optional argument

"falg" looks like a typo.

@nirvdrum nirvdrum force-pushed the yjit-compilation-log branch 4 times, most recently from f824a11 to 8b5e2bf Compare October 9, 2024 06:02
@nirvdrum
Copy link
Contributor Author

nirvdrum commented Oct 9, 2024

"falg" looks like a typo.

Thank you, @nobu. I've updated the PR description. I grepped the code and the commit log and couldn't find that particular typo anywhere else in this PR.

@maximecb
Copy link
Contributor

maximecb commented Oct 9, 2024

Testing out the compilation log output:

1728499309.025884: assign_score_of@/Users/maximecb/.gem/ruby/3.4.0/bundler/gems/liquid-4d4a565e98c1/lib/liquid/tags/assign.rb:50
1728499309.025957: assign_score_of@/Users/maximecb/.gem/ruby/3.4.0/bundler/gems/liquid-4d4a565e98c1/lib/liquid/tags/assign.rb:50
1728499309.026126: assign_score_of@/Users/maximecb/.gem/ruby/3.4.0/bundler/gems/liquid-4d4a565e98c1/lib/liquid/tags/assign.rb:50
1728499309.026158: assign_score_of@/Users/maximecb/.gem/ruby/3.4.0/bundler/gems/liquid-4d4a565e98c1/lib/liquid/tags/assign.rb:52
1728499309.026211: assign_score_of@/Users/maximecb/.gem/ruby/3.4.0/bundler/gems/liquid-4d4a565e98c1/lib/liquid/tags/assign.rb:52
1728499309.026278: assign_score_of@/Users/maximecb/.gem/ruby/3.4.0/bundler/gems/liquid-4d4a565e98c1/lib/liquid/tags/assign.rb:52
1728499309.026352: assign_score_of@/Users/maximecb/.gem/ruby/3.4.0/bundler/gems/liquid-4d4a565e98c1/lib/liquid/tags/assign.rb:52
1728499309.026460: assign_score_of@/Users/maximecb/.gem/ruby/3.4.0/bundler/gems/liquid-4d4a565e98c1/lib/liquid/tags/assign.rb:52

What does it mean when a method shows up multiple times in a row? Are we compiling the same entry point several times in a row?

In this case:

1728499309.052179: block (2 levels) in render_segment@/Users/maximecb/.gem/ruby/3.4.0/bundler/gems/liquid-4d4a565e98c1/lib/liquid/tags/for.rb:157
1728499309.052258: block (2 levels) in render_segment@/Users/maximecb/.gem/ruby/3.4.0/bundler/gems/liquid-4d4a565e98c1/lib/liquid/tags/for.rb:158
1728499309.052362: block (2 levels) in render_segment@/Users/maximecb/.gem/ruby/3.4.0/bundler/gems/liquid-4d4a565e98c1/lib/liquid/tags/for.rb:158
1728499309.052473: block (2 levels) in render_segment@/Users/maximecb/.gem/ruby/3.4.0/bundler/gems/liquid-4d4a565e98c1/lib/liquid/tags/for.rb:158
1728499309.052775: block (2 levels) in render_segment@/Users/maximecb/.gem/ruby/3.4.0/bundler/gems/liquid-4d4a565e98c1/lib/liquid/tags/for.rb:158

Does 2 levels mean chain depth 2? In which case, could we print block (depth 2)?

@k0kubun
Copy link
Member

k0kubun commented Oct 9, 2024

Does 2 levels mean chain depth 2? In which case, could we print block (depth 2)?

It's the depth of the Ruby block. It's just how CRuby labels block ISEQs.

What does it mean when a method shows up multiple times in a row? Are we compiling the same entry point several times in a row?

That Ruby code seems to have multiple basic blocks in the same line. If we want to clarify that, we could print the index to the instruction of an ISEQ.

@maximecb
Copy link
Contributor

maximecb commented Oct 9, 2024

Ok I got confused because I thought block (2 levels) was a reference to a YJIT block with chain depth 2, but it's actually a Ruby block.

Might be good to print the iseq index as Kokubun suggests.

@nirvdrum
Copy link
Contributor Author

nirvdrum commented Oct 9, 2024

That Ruby code seems to have multiple basic blocks in the same line. If we want to clarify that, we could print the index to the instruction of an ISEQ.

I took the log format from what we we're using in diassembly dumps. It doesn't need to be in this PR, but should we update as well?

@k0kubun
Copy link
Member

k0kubun commented Oct 9, 2024

I don't think it's worth modifying CRuby (or deviating from what it uses) for it. It's CRuby's common format, which is also used in backtraces.

@nirvdrum nirvdrum force-pushed the yjit-compilation-log branch from 2f9b591 to a36b7d2 Compare October 9, 2024 22:43
@nirvdrum
Copy link
Contributor Author

nirvdrum commented Oct 9, 2024

That Ruby code seems to have multiple basic blocks in the same line. If we want to clarify that, we could print the index to the instruction of an ISEQ.

I don't think it's worth modifying CRuby (or deviating from what it uses) for it. It's CRuby's common format, which is also used in backtraces.

I'm understanding this to mean that we don't want to modify the format in iseq_get_location. How do you want the index to appear in the log line? It impacts whether I just append text to the output to iseq_get_location or make a more invasive change to the formatting. A sample line of what you'd like to see in the log would help.

@nirvdrum nirvdrum force-pushed the yjit-compilation-log branch from a36b7d2 to 3f58773 Compare October 9, 2024 23:15

This comment has been minimized.

@k0kubun
Copy link
Member

k0kubun commented Oct 9, 2024

How do you want the index to appear in the log line?
A sample line of what you'd like to see in the log would help.

Since I'm personally used to adding stuff inside parens like (foo: xxx, bar: yyy) locally for debugging, I would like:

assign_score_of@/path/to/liquid-4d4a565e98c1/lib/liquid/tags/assign.rb:50 (insn_idx: 0, chain_depth: 1)

@nirvdrum nirvdrum force-pushed the yjit-compilation-log branch 2 times, most recently from 0b5eab6 to 6498c73 Compare October 10, 2024 02:56
@nirvdrum nirvdrum force-pushed the yjit-compilation-log branch from 6498c73 to 6b31ca8 Compare October 10, 2024 15:51
nirvdrum and others added 15 commits October 17, 2024 13:26
…ompilation log.

The compilation log will be populated as compilation events occur. If a directory is supplied, then a filename based on the PID will be used as the write target. If a file name is supplied instead, the log will be written to that file.
… sent to a file.

Previously, the two modes were treated as being exclusive of one another. However, it could be beneficial to log all events to a file while also allowing for direct access of the last N events via `RubyVM::YJIT.compilation_log`.
… in-memory log without printing it.

Co-authored-by: Randy Stauner <[email protected]>
@nirvdrum nirvdrum force-pushed the yjit-compilation-log branch from 6b31ca8 to 5f81f1b Compare October 17, 2024 17:46
Copy link
Contributor

@maximecb maximecb left a comment

Choose a reason for hiding this comment

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

Nicely done Kevin 🙏

@maximecb maximecb enabled auto-merge (squash) October 17, 2024 21:01
auto-merge was automatically disabled October 17, 2024 21:07

Head branch was pushed to by a user without write access

@nirvdrum nirvdrum force-pushed the yjit-compilation-log branch from 676210d to a76d27c Compare October 17, 2024 21:07
@maximecb maximecb enabled auto-merge (squash) October 17, 2024 21:36
@maximecb maximecb merged commit 158b8cb into ruby:master Oct 17, 2024
64 checks passed
@maximecb maximecb deleted the yjit-compilation-log branch October 17, 2024 21:36
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.

5 participants