ruby-changes:68966
From: Noah <ko1@a...>
Date: Thu, 21 Oct 2021 08:19:29 +0900 (JST)
Subject: [ruby-changes:68966] d2e9932908 (master): Convert YJIT stats reporting on exit from C to Ruby.
https://git.ruby-lang.org/ruby.git/commit/?id=d2e9932908 From d2e9932908cf1a1075d07157bfa520a1714d43b0 Mon Sep 17 00:00:00 2001 From: Noah Gibbs <noah.gibbs@s...> Date: Tue, 29 Jun 2021 16:36:05 +0100 Subject: Convert YJIT stats reporting on exit from C to Ruby. --- yjit.rb | 80 +++++++++++++++++++++++++++++++++----- yjit_iface.c | 125 ----------------------------------------------------------- 2 files changed, 70 insertions(+), 135 deletions(-) diff --git a/yjit.rb b/yjit.rb index 3998e4fda8..db283fe33b 100644 --- a/yjit.rb +++ b/yjit.rb @@ -139,18 +139,78 @@ module YJIT https://github.com/ruby/ruby/blob/trunk/yjit.rb#L139 # Format and print out counters def _print_stats - counters = runtime_stats - return unless counters + stats = runtime_stats + return unless stats $stderr.puts("***YJIT: Printing YJIT statistics on exit***") - $stderr.puts("Number of bindings allocated: %d\n" % counters[:binding_allocations]) - $stderr.puts("Number of locals modified through binding: %d\n" % counters[:binding_set]) - - print_counters(counters, prefix: 'send_', prompt: 'method call exit reasons: ') - print_counters(counters, prefix: 'leave_', prompt: 'leave exit reasons: ') - print_counters(counters, prefix: 'getivar_', prompt: 'getinstancevariable exit reasons:') - print_counters(counters, prefix: 'setivar_', prompt: 'setinstancevariable exit reasons:') - print_counters(counters, prefix: 'oaref_', prompt: 'opt_aref exit reasons: ') + $stderr.puts("Number of bindings allocated: %d\n" % stats[:binding_allocations]) + $stderr.puts("Number of locals modified through binding: %d\n" % stats[:binding_set]) + + print_counters(stats, prefix: 'send_', prompt: 'method call exit reasons: ') + print_counters(stats, prefix: 'leave_', prompt: 'leave exit reasons: ') + print_counters(stats, prefix: 'getivar_', prompt: 'getinstancevariable exit reasons:') + print_counters(stats, prefix: 'setivar_', prompt: 'setinstancevariable exit reasons:') + print_counters(stats, prefix: 'oaref_', prompt: 'opt_aref exit reasons: ') + + total_exits = total_exit_count(stats) + + # Number of instructions that finish executing in YJIT + retired_in_yjit = stats[:exec_instruction] - total_exits + + # Average length of instruction sequences executed by YJIT + avg_len_in_yjit = retired_in_yjit.to_f / total_exits + + # Proportion of instructions that retire in YJIT + total_insns_count = retired_in_yjit + stats[:vm_insns_count] + yjit_ratio_pct = 100.0 * retired_in_yjit.to_f / total_insns_count + + $stderr.puts "compiled_iseq_count: " + ("%10d" % stats[:compiled_iseq_count]) + $stderr.puts "inline_code_size: " + ("%10d" % stats[:inline_code_size]) + $stderr.puts "outlined_code_size: " + ("%10d" % stats[:outlined_code_size]) + + $stderr.puts "total_exit_count: " + ("%10d" % total_exits) + $stderr.puts "total_insns_count: " + ("%10d" % total_insns_count) + $stderr.puts "vm_insns_count: " + ("%10d" % stats[:vm_insns_count]) + $stderr.puts "yjit_insns_count: " + ("%10d" % stats[:exec_instruction]) + $stderr.puts "ratio_in_yjit: " + ("%9.1f" % yjit_ratio_pct) + "%" + $stderr.puts "avg_len_in_yjit: " + ("%10.1f" % avg_len_in_yjit) + + print_sorted_exit_counts(stats, prefix: "exit_") + end + + def print_sorted_exit_counts(stats, prefix:, how_many: 20, left_pad: 4) + exits = [] + stats.each do |k, v| + if k.start_with?(prefix) + exits.push [k.to_s.delete_prefix(prefix), v] + end + end + + exits = exits.sort_by { |name, count| -count }[0...how_many] + total_exits = total_exit_count(stats) + + top_n_total = exits.map { |name, count| count }.sum + top_n_exit_pct = top_n_total / total_exits + + $stderr.puts "Top-#{how_many} most frequent exit ops (#{"%.1f" % top_n_exit_pct}% of exits):" + + longest_insn_name_len = exits.map { |name, count| name.length }.max + exits.each do |name, count| + padding = longest_insn_name_len + left_pad + padded_name = "%#{padding}s" % name + padded_count = "%10d" % count + percent = 100.0 * count / total_exits + formatted_percent = "%.1f" % percent + $stderr.puts("#{padded_name}: #{padded_count} (#{formatted_percent})" ) + end + end + + def total_exit_count(stats, prefix: "exit_") + total = 0 + stats.each do |k,v| + total += v if k.start_with?(prefix) + end + total end def print_counters(counters, prefix:, prompt:) diff --git a/yjit_iface.c b/yjit_iface.c index e8a6dde969..a019458074 100644 --- a/yjit_iface.c +++ b/yjit_iface.c @@ -796,9 +796,6 @@ reset_stats_bang(rb_execution_context_t *ec, VALUE self) https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L796 #include "yjit.rbinc" -#if RUBY_DEBUG -// implementation for --yjit-stats - void rb_yjit_collect_vm_usage_insn(int insn) { @@ -825,128 +822,6 @@ rb_yjit_count_side_exit_op(const VALUE *exit_pc) https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L822 return exit_pc; // This function must return exit_pc! } -struct insn_count { - int64_t insn; - int64_t count; -}; - -static int -insn_count_sort_comp(const void *a, const void *b) -{ - const struct insn_count *count_a = a; - const struct insn_count *count_b = b; - if (count_a->count > count_b->count) { - return -1; - } - else if (count_a->count < count_b->count) { - return 1; - } - return 0; -} - -static struct insn_count insn_sorting_buffer[VM_INSTRUCTION_SIZE]; -static const struct insn_count * -sort_insn_count_array(int64_t *array) -{ - for (int i = 0; i < VM_INSTRUCTION_SIZE; i++) { - insn_sorting_buffer[i] = (struct insn_count) { i, array[i] }; - } - qsort(insn_sorting_buffer, VM_INSTRUCTION_SIZE, sizeof(insn_sorting_buffer[0]), &insn_count_sort_comp); - return insn_sorting_buffer; -} - -// Compute the total interpreter exit count -static int64_t -calc_total_exit_count() -{ - size_t total_exit_count = 0; - for (int i = 0; i < VM_INSTRUCTION_SIZE; i++) { - total_exit_count += exit_op_count[i]; - } - - return total_exit_count; -} - -static void -print_insn_count_buffer(int how_many, int left_pad) -{ - size_t total_exit_count = calc_total_exit_count(); - - // Sort the exit ops by decreasing frequency - const struct insn_count *sorted_exit_ops = sort_insn_count_array(exit_op_count); - - // Compute the longest instruction name and top10_exit_count - size_t longest_insn_len = 0; - size_t top10_exit_count = 0; - for (int i = 0; i < how_many; i++) { - const char *instruction_name = insn_name(sorted_exit_ops[i].insn); - size_t len = strlen(instruction_name); - if (len > longest_insn_len) { - longest_insn_len = len; - } - top10_exit_count += sorted_exit_ops[i].count; - } - - double top10_exit_percent = 100.0 * top10_exit_count / total_exit_count; - - fprintf(stderr, "top-%d most frequent exit ops (%.1f%% of exits):\n", how_many, top10_exit_percent); - - // Print the top-N most frequent exit counts - for (int i = 0; i < how_many; i++) { - const char *instruction_name = insn_name(sorted_exit_ops[i].insn); - size_t padding = left_pad + longest_insn_len - strlen(instruction_name); - for (size_t j = 0; j < padding; j++) { - fputc(' ', stderr); - } - double percent = 100 * sorted_exit_ops[i].count / (double)total_exit_count; - fprintf(stderr, "%s: %10" PRId64 " (%.1f%%)\n", instruction_name, sorted_exit_ops[i].count, percent); - } -} - -__attribute__((destructor)) -static void -print_yjit_stats(void) -{ - if (!rb_yjit_opts.gen_stats) { - return; - } - - // Warn if the executable code block is out of the relative - // 32-bit jump range away from compiled C code - ptrdiff_t start_diff = (cb->mem_block + cb->mem_size) - (uint8_t*)&print_yjit_stats; - if (start_diff < INT32_MIN || start_diff > INT32_MAX) { - fprintf(stderr, "WARNING: end of code block past rel32 offset range from C code\n"); - } - - // Compute the total exit count - int64_t total_exit_count = calc_total_exit_count(); - - // Number of instructions that finish executing in YJIT. See :count-placement:. - int64_t retired_in_yjit = yjit_runtime_counters.exec_instruction - total_exit_count; - - // Average length of instruction sequences executed by YJIT - double avg_len_in_yjit = (double)retired_in_yjit / total_exit_count; - - // Proportion of instructions that retire in YJIT - int64_t total_insns_count = retired_in_yjit + yjit_runtime_counters.vm_insns_count; - double ratio = retired_in_yjit / (double)total_insns_count; - - fprintf(stderr, "compiled_iseq_count: %10" PRId64 "\n", yjit_runtime_counters.compiled_iseq_count); - fprintf(stderr, "inline_code_size: %10d\n", cb->write_pos); - fprintf(stderr, "outlined_code_size: %10d\n", ocb->write_pos); - - fprintf(stderr, "total_exit_count: %10" PRId64 "\n", total_exit_count); - fprintf(stderr, "total_insns_count: %10" PRId64 "\n", total_insns_count); - fprintf(stderr, "vm_insns_count: %10" PRId64 "\n", yjit_runtime_counters.vm_insns_count); - fprintf(stderr, "yjit_insns_count: %10" PRId64 "\n", yjit_runtime_counters.exec_instruction); - fprintf(stderr, "ratio_in_yjit: %9.1f%%\n", ratio * 100); - fprintf(stderr, "avg_len_in_yjit: %10.1f\n", avg_len_in_yjit); - - // Print the top-N most frequent exit ops - print_insn_count_buffer(20, 4); -} -#endif // if RUBY_DEBUG - void rb_yjit_iseq_mark(const struct rb_iseq_constant_body *body) { -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/