Skip to content

Commit

Permalink
Add benchmark mode
Browse files Browse the repository at this point in the history
It uses Google Benchmark compatible JSON output.
  • Loading branch information
kou committed Feb 5, 2024
1 parent 206a929 commit e5aeb87
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 25 deletions.
22 changes: 15 additions & 7 deletions lib/grntest/base-result.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2012-2013 Kouhei Sutou <[email protected]>
# Copyright (C) 2012-2024 Sutou Kouhei <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand All @@ -17,16 +15,26 @@

module Grntest
class BaseResult
attr_accessor :elapsed_time
attr_accessor :cpu_elapsed_time
attr_accessor :real_elapsed_time
def initialize
@elapsed_time = 0
@cpu_elapsed_time = 0
@real_elapsed_time = 0
end

def measure
start_time = Time.now
cpu_start_time = Process.times
real_start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
yield
ensure
@elapsed_time = Time.now - start_time
cpu_finish_time = Process.times
real_finish_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@cpu_elapsed_time =
(cpu_finish_time.utime - cpu_start_time.utime) +
(cpu_finish_time.stime - cpu_start_time.stime) +
(cpu_finish_time.cutime - cpu_start_time.cutime) +
(cpu_finish_time.cstime - cpu_start_time.cstime)
@real_elapsed_time = real_finish_time - real_start_time
end
end
end
4 changes: 3 additions & 1 deletion lib/grntest/execution-context.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2012-2021 Sutou Kouhei <[email protected]>
# Copyright (C) 2012-2024 Sutou Kouhei <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -39,6 +39,7 @@ class ExecutionContext
attr_writer :collect_query_log
attr_writer :debug
attr_accessor :platform
attr_accessor :benchmarks
def initialize
@logging = true
@base_directory = Pathname(".")
Expand Down Expand Up @@ -70,6 +71,7 @@ def initialize
@collect_query_log = false
@debug = false
@platform = guess_platform
@benchmarks = []
end

def logging?
Expand Down
28 changes: 25 additions & 3 deletions lib/grntest/executors/base-executor.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2012-2023 Sutou Kouhei <[email protected]>
# Copyright (C) 2012-2024 Sutou Kouhei <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -48,6 +48,8 @@ def initialize(context)
@raw_status_response = nil
@features = nil
@substitutions = {}
@noop_benchmark_result = BenchmarkResult.new("noop", 1, 1)
@benchmark_result = @noop_benchmark_result
end

def execute(script_path)
Expand Down Expand Up @@ -520,6 +522,18 @@ def execute_directive_remove_substitution(line, content, options)
@substitutions.delete(pattern)
end

def execute_directive_start_benchmark(line, content, options)
_, n_items, n_iterations, name = content.split(" ", 4)
n_items = Integer(n_items, 10)
n_iterations = Integer(n_iterations, 10)
@benchmark_result = BenchmarkResult.new(name, n_items, n_iterations)
@context.benchmarks << @benchmark_result
end

def execute_directive_finish_benchmark(line, content, options)
@benchmark_result = @noop_benchmark_result
end

def execute_directive(parser, line, content)
command, *options = Shellwords.split(content)
case command
Expand Down Expand Up @@ -579,6 +593,10 @@ def execute_directive(parser, line, content)
execute_directive_add_substitution(line, content, options)
when "remove-substitution"
execute_directive_remove_substitution(line, content, options)
when "start-benchmark"
execute_directive_start_benchmark(line, content, options)
when "finish-benchmark"
execute_directive_finish_benchmark(line, content, options)
else
log_input(line)
log_error("#|e| unknown directive: <#{command}>")
Expand Down Expand Up @@ -628,8 +646,12 @@ def execute_command(command)
timeout = @context.timeout
response = nil
begin
Timeout.timeout(timeout) do
response = send_command(command)
@benchmark_result.measure do
@benchmark_result.n_iterations.times do
Timeout.timeout(timeout) do
response = send_command(command)
end
end
end
rescue Timeout::Error
log_error("# error: timeout (#{timeout}s)")
Expand Down
17 changes: 10 additions & 7 deletions lib/grntest/reporters.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Sutou Kouhei <[email protected]>
# Copyright (C) 2012-2024 Sutou Kouhei <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand All @@ -13,27 +13,30 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

require "grntest/reporters/mark-reporter"
require "grntest/reporters/benchmark-json-reporter"
require "grntest/reporters/buffered-mark-reporter"
require "grntest/reporters/stream-reporter"
require "grntest/reporters/inplace-reporter"
require "grntest/reporters/mark-reporter"
require "grntest/reporters/progress-reporter"
require "grntest/reporters/stream-reporter"

module Grntest
module Reporters
class << self
def create_reporter(tester)
case tester.reporter
when :mark
MarkReporter.new(tester)
when :"benchmark-json"
BenchmarkJSONReporter.new(tester)
when :"buffered-mark"
BufferedMarkReporter.new(tester)
when :stream
StreamReporter.new(tester)
when :inplace
InplaceReporter.new(tester)
when :mark
MarkReporter.new(tester)
when :progress
ProgressReporter.new(tester)
when :stream
StreamReporter.new(tester)
end
end
end
Expand Down
10 changes: 5 additions & 5 deletions lib/grntest/reporters/base-reporter.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2012-2023 Sutou Kouhei <[email protected]>
# Copyright (C) 2012-2024 Sutou Kouhei <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -45,7 +45,7 @@ def report_summary(result)
puts(statistics_header)
puts(colorize(statistics(result), result))
pass_ratio = result.pass_ratio
elapsed_time = result.elapsed_time
elapsed_time = result.real_elapsed_time
summary = "%.4g%% passed in %.4fs." % [pass_ratio, elapsed_time]
puts(colorize(summary, result))
end
Expand Down Expand Up @@ -78,10 +78,10 @@ def statistics(result)
end

def throughput(result)
if result.elapsed_time.zero?
if result.real_elapsed_time.zero?
tests_per_second = 0
else
tests_per_second = result.n_tests / result.elapsed_time
tests_per_second = result.n_tests / result.real_elapsed_time
end
tests_per_second
end
Expand Down Expand Up @@ -160,7 +160,7 @@ def report_test_result(result, label)
end

def test_result_message(result, label)
elapsed_time = result.elapsed_time
elapsed_time = result.real_elapsed_time
formatted_elapsed_time = "%.4fs" % elapsed_time
formatted_elapsed_time = colorize(formatted_elapsed_time,
elapsed_time_status(elapsed_time))
Expand Down
121 changes: 121 additions & 0 deletions lib/grntest/reporters/benchmark-json-reporter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Copyright (C) 2024 Sutou Kouhei <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

require "json"
require "time"

require "grntest/reporters/base-reporter"

module Grntest
module Reporters
class BenchmarkJSONReporter < BaseReporter
def initialize(tester)
super
end

def on_start(result)
puts(<<-JSON)
{
"context": {"
"date": #{Time.now.iso8601.to_json},
"host_name": #{Socket.gethostname.to_json},
"executable": #{@tester.testee.to_json},
"num_cpus": #{Etc.nprocessors},
JSON
cpu_cycles_per_second = detect_cpu_cycles_per_second
if cpu_cycles_per_second
puts(<<-JSON)
"mhz_per_cpu": #{cpu_cycles_per_second / 1_000_000.0},
JSON
end
puts(<<-JSON)
"caches": []
},
"benchmarks": [
JSON
end

def on_worker_start(worker)
end

def on_suite_start(worker)
end

def on_test_start(worker)
end

def on_test_success(worker, result)
end

def on_test_failure(worker, result)
end

def on_test_leak(worker, result)
end

def on_test_omission(worker, result)
end

def on_test_no_check(worker, result)
end

def on_test_finish(worker, result)
benchmarks = result.benchmarks.collect do |benchmark|
<<-JSON.chomp
{
"name": #{result.test_name.to_json},
"run_name": #{benchmark.name.to_json},
"run_type": "iteration",
"iterations": #{benchmark.n_iterations},
"real_time": #{benchmark.real_elapsed_time},
"cpu_time": #{benchmark.cpu_elapsed_time},
"time_unit": "s",
"items_per_second": #{benchmark.items_per_second},
}
JSON
end
puts(benchmarks.join(",\n"))
end

def on_suite_finish(worker)
end

def on_worker_finish(worker)
end

def on_finish(result)
puts(<<-JSON)
]
}
JSON
end

private
def detect_cpu_cycles_per_second
if File.exist?("/proc/cpuinfo")
File.open("/proc/cpuinfo") do |cpuinfo|
cpuinfo.each_line do |line|
case line
when /\Acpu MHz\s+: ([\d.]+)/
return Float($1)
end
end
end
end
nil
end
end
end
end
21 changes: 20 additions & 1 deletion lib/grntest/test-runner.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2012-2022 Sutou Kouhei <[email protected]>
# Copyright (C) 2012-2024 Sutou Kouhei <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -28,10 +28,27 @@
require "grntest/base-result"

module Grntest
class BenchmarkResult < BaseResult
attr_reader :name
attr_reader :n_items
attr_reader :n_iterations
def initialize(name, n_items, n_iterations)
super()
@name = name
@n_items = n_items
@n_iterations = n_iterations
end

def items_per_second
@n_items / @real_elapsed_time
end
end

class TestResult < BaseResult
attr_accessor :worker_id, :test_name
attr_accessor :expected, :actual, :n_leaked_objects
attr_writer :omitted
attr_accessor :benchmarks
def initialize(worker)
super()
@worker_id = worker.id
Expand All @@ -40,6 +57,7 @@ def initialize(worker)
@expected = nil
@n_leaked_objects = 0
@omitted = false
@benchmarks = []
end

def status
Expand Down Expand Up @@ -159,6 +177,7 @@ def execute_groonga_script(result)
check_memory_leak(context)
result.omitted = context.omitted?
result.actual = context.result
result.benchmarks = context.benchmarks
context.close_logs
end
end
Expand Down
Loading

0 comments on commit e5aeb87

Please sign in to comment.