Skip to content

Commit d76120b

Browse files
committed
fix: clean up more debug output; more consistent benchmark reporting logic
1 parent 9c598fe commit d76120b

File tree

9 files changed

+136
-128
lines changed

9 files changed

+136
-128
lines changed

Justfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ test:
44
ruby -Itest test/test_tracer.rb
55

66
bench name="heavy_work" write_report="console":
7-
ruby test/benchmarks/run_benchmark.rb {{name}} --write-report={{write_report}}
7+
ruby test/benchmarks/run_benchmarks.rb test/benchmarks/programs --write-report={{write_report}}
88

99
build-extension:
1010
cargo build --release --manifest-path gems/native-tracer/ext/native_tracer/Cargo.toml

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ however you probably want to use it in combination with CodeTracer, which would
3030

3131
### ENV variables
3232

33-
* if you pass `CODETRACER_RUBY_TRACER_DEBUG=1`, you enables some additional debug-related logging
33+
* if you pass `CODETRACER_RUBY_RECORDER_DEBUG=1`, you enables some additional debug-related logging
3434
* `CODETRACER_RUBY_RECORDER_OUT_DIR` can be used to specify the directory for trace files
3535

3636
## Future directions

gems/pure-ruby-tracer/lib/recorder.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,6 @@ def to_data_for_json
103103

104104
NONE_TYPE_SPECIFIC_INFO = {kind: 'None'}
105105

106-
$STEP_COUNT = 0
107-
108106
class TraceRecord
109107
# part of the final trace
110108
attr_accessor :steps, :calls, :variables, :events, :types, :flow, :paths
@@ -139,6 +137,8 @@ def initialize
139137
@t3 = nil
140138
@codetracer_id = 0
141139
@debug = false
140+
141+
@step_count = 0
142142
end
143143

144144
def load_flow(path, line, binding)
@@ -173,9 +173,9 @@ def register_step(path, line)
173173
step_record = StepRecord.new(self.path_id(path), line)
174174
@events << [:Step, step_record] # because we convert later to {Step: step-record}: default enum json format in serde/rust
175175
# $stderr.write path, "\n"
176-
$STEP_COUNT += 1
177-
if $STEP_COUNT % 1_000 == 0
178-
$stdout.write "steps ", $STEP_COUNT, "\n"
176+
if @debug
177+
@step_count += 1
178+
$stdout.write "steps ", $STEP_COUNT, "\n" if $STEP_COUNT % 1_000 == 0
179179
end
180180
end
181181

gems/pure-ruby-tracer/lib/trace.rb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class Tracer
7272

7373
attr_reader :ignore_list, :record, :debug
7474

75-
def initialize(record, debug: ENV['CODETRACER_RUBY_TRACER_DEBUG'] == '1')
75+
def initialize(record, debug: false)
7676
@tracing = false
7777
@trace_stopped = false
7878
@record = record
@@ -250,10 +250,9 @@ def load_variables(binding)
250250
end
251251
end
252252

253-
254-
$tracer = Tracer.new($codetracer_record)
255-
256253
if __FILE__ == $PROGRAM_NAME
254+
$tracer = Tracer.new($codetracer_record, debug: ENV['CODETRACER_RUBY_RECORDER_DEBUG'] == '1')
255+
257256
options = {}
258257
parser = OptionParser.new do |opts|
259258
opts.banner = "usage: ruby trace.rb [options] <program> [args]"

test/benchmarks/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ This directory contains programs and fixtures used for manual performance testin
22

33
These benchmarks are **not** executed in CI because they may take longer to run than typical tests. Each program has a reference trace recorded using the current tracer implementation. When we experiment with alternative tracer implementations, we will compare their output with these fixtures to ensure compatibility.
44

5-
The reference traces are stored via Git LFS so the repository stays lightweight. `run_benchmark.rb` verifies the SHA-256 hash of each fixture and downloads it with `git lfs` on demand if missing.
5+
The reference traces are stored via Git LFS so the repository stays lightweight.
66

77
At the moment there is a single benchmark (`heavy_work`) that exercises a mixture of array and hash operations while computing prime numbers. More benchmarks will be added as we expand the suite.
88

9-
Use `run_benchmark.rb --write-report=console BENCHMARK` to execute a single benchmark and print the runtime. Passing a path ending with `.json` or `.svg` will run all benchmarks and write a report in the chosen format.
9+
Use `run_benchmarks.rb --write-report=console BENCHMARKS_DIR` to execute a single benchmark and print the runtime. Passing a path ending with `.json` or `.svg` will run all benchmarks and write a report in the chosen format.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:912fc0347cb8a57abd94a7defd76b147f3a79e556745e45207b89529f8a59d8b
3-
size 8203587
2+
oid sha256:b6af4ae851481a7803436d6f5b398d8f0af305a03d55c14caa8e8ae4278254c4
3+
size 210704921

test/benchmarks/programs/heavy_work.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def primes(n)
1717
end
1818

1919
def compute
20-
arr = primes(100)
20+
arr = primes(500)
2121
hash = arr.each_with_index.to_h
2222
sum = arr.reduce(:+)
2323
str = arr.map(&:to_s).join(',')

test/benchmarks/run_benchmark.rb

Lines changed: 0 additions & 112 deletions
This file was deleted.

test/benchmarks/run_benchmarks.rb

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
require 'json'
5+
require 'fileutils'
6+
require 'digest'
7+
require 'benchmark'
8+
require 'optparse'
9+
10+
PROGRAMS_DIR = File.expand_path('programs', __dir__)
11+
FIXTURES_DIR = File.expand_path('fixtures', __dir__)
12+
TMP_DIR = File.expand_path('tmp', __dir__)
13+
WRITE_REPORT_DEFAULT = 'console'
14+
15+
options = { write_report: WRITE_REPORT_DEFAULT }
16+
OptionParser.new do |opts|
17+
opts.banner = 'Usage: ruby run_benchmarks.rb BENCHMARK_DIR [options]'
18+
opts.on('--write-report=DEST', 'console or path to .json/.svg report') do |dest|
19+
options[:write_report] = dest
20+
end
21+
end.parse!
22+
23+
benchmark_dir = ARGV.shift || abort('Usage: ruby run_benchmarks.rb BENCHMARK_DIR [options]')
24+
unless Dir.exist?(benchmark_dir)
25+
abort("Benchmark directory not found: #{benchmark_dir}")
26+
end
27+
28+
# Collect benchmark names (file basenames without extension)
29+
benchmarks = Dir.glob(File.join(benchmark_dir, '*.rb')).map { |f| File.basename(f, '.rb') }
30+
if benchmarks.empty?
31+
abort("No benchmark files (*.rb) found in directory: #{benchmark_dir}")
32+
end
33+
34+
# Compare two files for identical content
35+
def files_identical?(a, b)
36+
cmp_result = system('cmp', '-s', a, b)
37+
return $?.success? if !cmp_result.nil?
38+
File.binread(a) == File.binread(b)
39+
end
40+
41+
# Run a single benchmark by name
42+
def run_benchmark(name, benchmark_dir)
43+
program = File.expand_path(File.join(benchmark_dir, "#{name}.rb"))
44+
fixture = File.join(FIXTURES_DIR, "#{name}_trace.json")
45+
output_dir = File.join(TMP_DIR, name)
46+
47+
FileUtils.mkdir_p(output_dir)
48+
raise 'Reference trace unavailable' unless File.exist?(fixture)
49+
50+
elapsed = Benchmark.realtime do
51+
system('ruby', File.expand_path('../../gems/pure-ruby-tracer/lib/trace.rb', __dir__),
52+
'--out-dir', output_dir,
53+
program)
54+
raise 'Trace failed' unless $?.success?
55+
end
56+
runtime_ms = (elapsed * 1000).round
57+
output_trace = File.join(output_dir, 'trace.json')
58+
success = files_identical?(fixture, output_trace)
59+
size_bytes = File.size(output_trace)
60+
61+
{ name: name, runtime_ms: runtime_ms, trace_size: size_bytes, success: success }
62+
end
63+
64+
# Execute all benchmarks
65+
results = benchmarks.map { |b| run_benchmark(b, benchmark_dir) }
66+
67+
# Reporting
68+
if options[:write_report] == 'console'
69+
# Determine column widths
70+
name_w = [ 'Benchmark'.length, *results.map { |r| r[:name].length } ].max
71+
rt_w = [ 'Runtime'.length, *results.map { |r| r[:runtime_ms].to_s.length } ].max
72+
ts_w = [ 'Trace Size'.length, *results.map { |r| r[:trace_size].to_s.length } ].max
73+
74+
# Header
75+
printf "%-#{name_w}s %#{rt_w}s %#{ts_w}s %s\n", 'Benchmark', 'Runtime', 'Trace Size', 'Status'
76+
puts '-' * (name_w + rt_w + ts_w + 10)
77+
78+
# Rows
79+
results.each do |r|
80+
status = r[:success] ? 'OK' : 'FAIL'
81+
printf "%-#{name_w}s %#{rt_w}d ms %#{ts_w}d %s\n", r[:name], r[:runtime_ms], r[:trace_size], status
82+
end
83+
84+
# Exit with non-zero if any failed
85+
exit 1 unless results.all? { |r| r[:success] }
86+
else
87+
dest = options[:write_report]
88+
FileUtils.mkdir_p(File.dirname(dest))
89+
90+
case File.extname(dest)
91+
when '.json'
92+
data = results.map { |r| { benchmark: r[:name], runtime_ms: r[:runtime_ms], trace_bytes: r[:trace_size] } }
93+
File.write(dest, JSON.pretty_generate(data))
94+
when '.svg'
95+
row_height = 25
96+
height = 40 + row_height * results.size
97+
svg = +"<svg xmlns='http://www.w3.org/2000/svg' width='500' height='\#{height}'>\n"
98+
svg << " <foreignObject width='100%' height='100%'>\n"
99+
svg << " <style>table{border-collapse:collapse;font-family:sans-serif;}td,th{border:1px solid #999;padding:4px;}</style>\n"
100+
svg << " <table>\n"
101+
svg << " <thead><tr><th>Benchmark</th><th>Runtime (ms)</th><th>Trace size (bytes)</th><th>Status</th></tr></thead>\n"
102+
svg << " <tbody>\n"
103+
results.each do |r|
104+
status = r[:success] ? 'OK' : 'FAIL'
105+
svg << " <tr><td>#{r[:name]}</td><td>#{r[:runtime_ms]}</td><td>#{r[:trace_size]}</td><td>#{status}</td></tr>\n"
106+
end
107+
svg << " </tbody>\n"
108+
svg << " </table>\n"
109+
svg << " </foreignObject>\n"
110+
svg << "</svg>\n"
111+
File.write(dest, svg)
112+
else
113+
abort "Unknown report format '\#{dest}'"
114+
end
115+
116+
# Warn and exit if any failures
117+
unless results.all? { |r| r[:success] }
118+
warn 'One or more traces differ from reference!'
119+
exit 1
120+
end
121+
end

0 commit comments

Comments
 (0)