mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Convert YJIT stats reporting on exit from C to Ruby.
This commit is contained in:
parent
33227b1094
commit
d2e9932908
2 changed files with 69 additions and 134 deletions
78
yjit.rb
78
yjit.rb
|
@ -139,18 +139,78 @@ module YJIT
|
|||
|
||||
# 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])
|
||||
$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(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: ')
|
||||
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:)
|
||||
|
|
125
yjit_iface.c
125
yjit_iface.c
|
@ -796,9 +796,6 @@ reset_stats_bang(rb_execution_context_t *ec, VALUE self)
|
|||
|
||||
#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)
|
|||
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)
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue