module ZJIT
此模块允许对 CRuby 的即时编译器 ZJIT 进行自省。模块中的所有内容都高度依赖于实现,并且与标准库相比,API 可能不太稳定。
如果 ZJIT 不支持 CRuby 所构建的特定平台,则此模块可能不存在。
Public Class Methods
Source
# File zjit.rb, line 140 def dump_exit_locations(filename) unless trace_exit_locations_enabled? raise ArgumentError, "--zjit-trace-exits must be enabled to use dump_exit_locations." end File.open(filename, "wb") do |file| Marshal.dump(RubyVM::ZJIT.exit_locations, file) file.size end end
Marshal 将退出位置转储到指定的文件名。
用法
在脚本调用中
RubyVM::ZJIT.dump_exit_locations("my_file.dump")
然后使用以下选项运行文件
ruby --zjit --zjit-stats --zjit-trace-exits test.rb
代码运行完毕后,使用 Stackprof 读取转储文件。有关选项,请参阅 Stackprof 文档。
Source
# File zjit.rb, line 31 def enable return false if enabled? if Primitive.cexpr! 'RBOOL(rb_yjit_enabled_p)' warn("Only one JIT can be enabled at the same time.") return false end Primitive.rb_zjit_enable end
启用 ZJIT 编译。
Source
# File zjit.rb, line 26 def enabled? Primitive.cexpr! 'RBOOL(rb_zjit_enabled_p)' end
检查 ZJIT 是否已启用
Source
# File zjit.rb, line 51 def exit_locations return unless trace_exit_locations_enabled? results = Primitive.rb_zjit_get_exit_locations raw_samples = results[:raw] line_samples = results[:lines] frames = results[:frames] samples_count = 0 # Use nonexistent.def as a dummy file name. frame_template = { samples: 0, total_samples: 0, edges: {}, name: name, file: "nonexistent.def", line: nil, lines: {} } # Loop through all possible instructions and setup the frame hash. RubyVM::INSTRUCTION_NAMES.each_with_index do |name, frame_id| frames[frame_id] = frame_template.dup.tap { |h| h[:name] = name } end # Loop through the raw_samples and build the hashes for StackProf. # The loop is based off an example in the StackProf documentation and therefore # this functionality can only work with that library. # # Raw Samples: # [ length, frame1, frame2, frameN, ..., instruction, count # # Line Samples # [ length, line_1, line_2, line_n, ..., dummy value, count i = 0 while i < raw_samples.length stack_length = raw_samples[i] i += 1 # consume the stack length sample_count = raw_samples[i + stack_length] prev_frame_id = nil stack_length.times do |idx| idx += i frame_id = raw_samples[idx] if prev_frame_id prev_frame = frames[prev_frame_id] prev_frame[:edges][frame_id] ||= 0 prev_frame[:edges][frame_id] += sample_count end frame_info = frames[frame_id] frame_info[:total_samples] += sample_count frame_info[:lines][line_samples[idx]] ||= [0, 0] frame_info[:lines][line_samples[idx]][0] += sample_count prev_frame_id = frame_id end i += stack_length # consume the stack top_frame_id = prev_frame_id top_frame_line = 1 frames[top_frame_id][:samples] += sample_count frames[top_frame_id][:lines] ||= {} frames[top_frame_id][:lines][top_frame_line] ||= [0, 0] frames[top_frame_id][:lines][top_frame_line][1] += sample_count samples_count += sample_count i += 1 end results[:samples] = samples_count # These values are mandatory to include for stackprof, but we don't use them. results[:missed_samples] = 0 results[:gc_samples] = 0 results end
如果启用了 –zjit-trace-exits,则将 Primitive.rb_zjit_get_exit_locations 中的哈希解析为 Stackprof 可读的格式。这将使我们能够根据正在退出的指令来查找 ZJIT 中退出点的确切位置。
Source
# File zjit.rb, line 162 def reset_stats! Primitive.rb_zjit_reset_stats_bang end
丢弃为 ‘–zjit-stats` 收集的统计信息。
Source
# File zjit.rb, line 157 def stats(target_key = nil) Primitive.rb_zjit_stats(target_key) end
将 ZJIT 统计信息返回为 Hash
Source
# File zjit.rb, line 152 def stats_enabled? Primitive.rb_zjit_stats_enabled_p end
检查是否使用了 ‘–zjit-stats`
Source
# File zjit.rb, line 167 def stats_string buf = +"***ZJIT: Printing ZJIT statistics on exit***\n" stats = self.stats if stats[:guard_type_count].nonzero? stats[:guard_type_exit_ratio] = stats[:exit_guard_type_failure].to_f / stats[:guard_type_count] * 100 end if stats[:guard_shape_count].nonzero? stats[:guard_shape_exit_ratio] = stats[:exit_guard_shape_failure].to_f / stats[:guard_shape_count] * 100 end # Show counters independent from exit_* or dynamic_send_* print_counters_with_prefix(prefix: 'not_inlined_cfuncs_', prompt: 'not inlined C methods', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'ccall_', prompt: 'calls to C functions from JIT code', buf:, stats:, limit: 20) # Don't show not_annotated_cfuncs right now because it mostly duplicates not_inlined_cfuncs # print_counters_with_prefix(prefix: 'not_annotated_cfuncs_', prompt: 'not annotated C methods', buf:, stats:, limit: 20) # Show fallback counters, ordered by the typical amount of fallbacks for the prefix at the time print_counters_with_prefix(prefix: 'unspecialized_send_def_type_', prompt: 'not optimized method types for send', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'unspecialized_send_without_block_def_type_', prompt: 'not optimized method types for send_without_block', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'uncategorized_fallback_yarv_insn_', prompt: 'instructions with uncategorized fallback reason', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'send_fallback_', prompt: 'send fallback reasons', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'setivar_fallback_', prompt: 'setivar fallback reasons', buf:, stats:, limit: 5) print_counters_with_prefix(prefix: 'getivar_fallback_', prompt: 'getivar fallback reasons', buf:, stats:, limit: 5) print_counters_with_prefix(prefix: 'definedivar_fallback_', prompt: 'definedivar fallback reasons', buf:, stats:, limit: 5) print_counters_with_prefix(prefix: 'invokeblock_handler_', prompt: 'invokeblock handler', buf:, stats:, limit: 10) # Show most popular unsupported call features. Because each call can # use multiple complex features, a decrease in this number does not # necessarily mean an increase in number of optimized calls. print_counters_with_prefix(prefix: 'complex_arg_pass_', prompt: 'popular complex argument-parameter features not optimized', buf:, stats:, limit: 10) # Show exit counters, ordered by the typical amount of exits for the prefix at the time print_counters_with_prefix(prefix: 'compile_error_', prompt: 'compile error reasons', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'unhandled_yarv_insn_', prompt: 'unhandled YARV insns', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'unhandled_hir_insn_', prompt: 'unhandled HIR insns', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'exit_', prompt: 'side exit reasons', buf:, stats:, limit: 20) # Show no-prefix counters, having the most important stat `ratio_in_zjit` at the end print_counters([ :send_count, :dynamic_send_count, :optimized_send_count, :dynamic_setivar_count, :dynamic_getivar_count, :dynamic_definedivar_count, :iseq_optimized_send_count, :inline_cfunc_optimized_send_count, :inline_iseq_optimized_send_count, :non_variadic_cfunc_optimized_send_count, :variadic_cfunc_optimized_send_count, ], buf:, stats:, right_align: true, base: :send_count) print_counters([ :compiled_iseq_count, :failed_iseq_count, :compile_time_ns, :profile_time_ns, :gc_time_ns, :invalidation_time_ns, :vm_write_pc_count, :vm_write_sp_count, :vm_write_locals_count, :vm_write_stack_count, :vm_write_to_parent_iseq_local_count, :vm_read_from_parent_iseq_local_count, :guard_type_count, :guard_type_exit_ratio, :guard_shape_count, :guard_shape_exit_ratio, :code_region_bytes, :zjit_alloc_bytes, :total_mem_bytes, :side_exit_count, :total_insn_count, :vm_insn_count, :zjit_insn_count, :ratio_in_zjit, ], buf:, stats:) buf end
将 ZJIT 统计信息的摘要作为 String 获取
Source
# File zjit.rb, line 43 def trace_exit_locations_enabled? Primitive.rb_zjit_trace_exit_locations_enabled_p end
检查是否使用了 ‘–zjit-trace-exits`