benchmark_driver_monotonic_raw 0.14.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/.travis.yml +16 -0
- data/CHANGELOG.md +357 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +386 -0
- data/Rakefile +9 -0
- data/benchmark-driver/.gitignore +12 -0
- data/benchmark-driver/CODE_OF_CONDUCT.md +74 -0
- data/benchmark-driver/Gemfile +6 -0
- data/benchmark-driver/LICENSE.txt +21 -0
- data/benchmark-driver/README.md +8 -0
- data/benchmark-driver/Rakefile +1 -0
- data/benchmark-driver/benchmark-driver.gemspec +21 -0
- data/benchmark-driver/bin/console +14 -0
- data/benchmark-driver/bin/setup +8 -0
- data/benchmark-driver/lib/benchmark-driver.rb +1 -0
- data/benchmark-driver/lib/benchmark/driver.rb +1 -0
- data/benchmark_driver.gemspec +28 -0
- data/bin/console +7 -0
- data/bin/setup +8 -0
- data/exe/benchmark-driver +118 -0
- data/images/optcarrot.png +0 -0
- data/lib/benchmark_driver.rb +14 -0
- data/lib/benchmark_driver/bulk_output.rb +59 -0
- data/lib/benchmark_driver/config.rb +59 -0
- data/lib/benchmark_driver/default_job.rb +29 -0
- data/lib/benchmark_driver/default_job_parser.rb +91 -0
- data/lib/benchmark_driver/job_parser.rb +55 -0
- data/lib/benchmark_driver/metric.rb +79 -0
- data/lib/benchmark_driver/output.rb +88 -0
- data/lib/benchmark_driver/output/compare.rb +216 -0
- data/lib/benchmark_driver/output/markdown.rb +107 -0
- data/lib/benchmark_driver/output/record.rb +61 -0
- data/lib/benchmark_driver/output/simple.rb +103 -0
- data/lib/benchmark_driver/rbenv.rb +25 -0
- data/lib/benchmark_driver/repeater.rb +52 -0
- data/lib/benchmark_driver/ruby_interface.rb +83 -0
- data/lib/benchmark_driver/runner.rb +103 -0
- data/lib/benchmark_driver/runner/command_stdout.rb +118 -0
- data/lib/benchmark_driver/runner/ips.rb +259 -0
- data/lib/benchmark_driver/runner/memory.rb +150 -0
- data/lib/benchmark_driver/runner/once.rb +118 -0
- data/lib/benchmark_driver/runner/recorded.rb +73 -0
- data/lib/benchmark_driver/runner/ruby_stdout.rb +146 -0
- data/lib/benchmark_driver/runner/time.rb +20 -0
- data/lib/benchmark_driver/struct.rb +98 -0
- data/lib/benchmark_driver/version.rb +3 -0
- metadata +150 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
class BenchmarkDriver::Output::Markdown
|
2
|
+
NAME_LENGTH = 8
|
3
|
+
|
4
|
+
# @param [Array<BenchmarkDriver::Metric>] metrics
|
5
|
+
# @param [Array<BenchmarkDriver::Job>] jobs
|
6
|
+
# @param [Array<BenchmarkDriver::Context>] contexts
|
7
|
+
def initialize(metrics:, jobs:, contexts:)
|
8
|
+
@metrics = metrics
|
9
|
+
@context_names = contexts.map(&:name)
|
10
|
+
@name_length = jobs.map(&:name).map(&:size).max
|
11
|
+
end
|
12
|
+
|
13
|
+
def with_warmup(&block)
|
14
|
+
without_stdout_buffering do
|
15
|
+
$stdout.print 'warming up'
|
16
|
+
block.call
|
17
|
+
end
|
18
|
+
ensure
|
19
|
+
$stdout.puts
|
20
|
+
end
|
21
|
+
|
22
|
+
def with_benchmark(&block)
|
23
|
+
@with_benchmark = true
|
24
|
+
without_stdout_buffering do
|
25
|
+
# Show header
|
26
|
+
$stdout.puts "# #{@metrics.first.name} (#{@metrics.first.unit})\n\n"
|
27
|
+
|
28
|
+
# Show executable names
|
29
|
+
$stdout.print("|#{' ' * @name_length} ")
|
30
|
+
@context_names.each do |context_name|
|
31
|
+
$stdout.print("|%#{NAME_LENGTH}s" % context_name) # same size as humanize
|
32
|
+
end
|
33
|
+
$stdout.puts('|')
|
34
|
+
|
35
|
+
# Show header separator
|
36
|
+
$stdout.print("|:#{'-' * (@name_length - 1)}--")
|
37
|
+
@context_names.each do |context_name|
|
38
|
+
$stdout.print("|:#{'-' * (NAME_LENGTH - 1)}") # same size as humanize
|
39
|
+
end
|
40
|
+
$stdout.puts('|')
|
41
|
+
|
42
|
+
block.call
|
43
|
+
end
|
44
|
+
rescue
|
45
|
+
@with_benchmark = false
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param [BenchmarkDriver::Job] job
|
49
|
+
def with_job(job, &block)
|
50
|
+
if @with_benchmark
|
51
|
+
$stdout.print("|%-#{@name_length}s " % job.name)
|
52
|
+
end
|
53
|
+
block.call
|
54
|
+
ensure
|
55
|
+
if @with_benchmark
|
56
|
+
$stdout.puts('|')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# @param [BenchmarkDriver::Context] context
|
61
|
+
def with_context(context, &block)
|
62
|
+
block.call
|
63
|
+
end
|
64
|
+
|
65
|
+
# @param [BenchmarkDriver::Result] result
|
66
|
+
def report(result)
|
67
|
+
if @with_benchmark
|
68
|
+
$stdout.print("|%#{NAME_LENGTH}s" % humanize(result.values.fetch(@metrics.first)))
|
69
|
+
else
|
70
|
+
$stdout.print '.'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# benchmark_driver ouputs logs ASAP. This enables sync flag for it.
|
77
|
+
def without_stdout_buffering
|
78
|
+
sync, $stdout.sync = $stdout.sync, true
|
79
|
+
yield
|
80
|
+
ensure
|
81
|
+
$stdout.sync = sync
|
82
|
+
end
|
83
|
+
|
84
|
+
def humanize(value)
|
85
|
+
if BenchmarkDriver::Result::ERROR.equal?(value)
|
86
|
+
return " %#{NAME_LENGTH}s" % 'ERROR'
|
87
|
+
elsif value == 0.0
|
88
|
+
return " %#{NAME_LENGTH}.3f" % 0.0
|
89
|
+
elsif value < 0
|
90
|
+
raise ArgumentError.new("Negative value: #{value.inspect}")
|
91
|
+
end
|
92
|
+
|
93
|
+
scale = (Math.log10(value) / 3).to_i
|
94
|
+
prefix = "%#{NAME_LENGTH}.3f" % (value.to_f / (1000 ** scale))
|
95
|
+
suffix =
|
96
|
+
case scale
|
97
|
+
when 1; 'k'
|
98
|
+
when 2; 'M'
|
99
|
+
when 3; 'G'
|
100
|
+
when 4; 'T'
|
101
|
+
when 5; 'Q'
|
102
|
+
else # < 1000 or > 10^15, no scale or suffix
|
103
|
+
return " #{prefix}"
|
104
|
+
end
|
105
|
+
"#{prefix}#{suffix}"
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class BenchmarkDriver::Output::Record
|
2
|
+
# @param [Array<BenchmarkDriver::Metric>] metrics
|
3
|
+
# @param [Array<BenchmarkDriver::Job>] jobs
|
4
|
+
# @param [Array<BenchmarkDriver::Context>] contexts
|
5
|
+
def initialize(metrics:, jobs:, contexts:)
|
6
|
+
@metrics = metrics
|
7
|
+
@job_warmup_context_result = Hash.new do |h1, job|
|
8
|
+
h1[job] = Hash.new do |h2, warmup|
|
9
|
+
h2[warmup] = Hash.new do |h3, context|
|
10
|
+
h3[context] = {}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def with_warmup(&block)
|
17
|
+
$stdout.print 'warming up'
|
18
|
+
block.call
|
19
|
+
ensure
|
20
|
+
$stdout.puts
|
21
|
+
end
|
22
|
+
|
23
|
+
def with_benchmark(&block)
|
24
|
+
@with_benchmark = true
|
25
|
+
$stdout.print 'benchmarking'
|
26
|
+
block.call
|
27
|
+
ensure
|
28
|
+
$stdout.puts
|
29
|
+
@with_benchmark = false
|
30
|
+
save_record
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param [BenchmarkDriver::Job] job
|
34
|
+
def with_job(job, &block)
|
35
|
+
@job = job
|
36
|
+
block.call
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param [BenchmarkDriver::Context] context
|
40
|
+
def with_context(context, &block)
|
41
|
+
@context = context
|
42
|
+
block.call
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param [BenchmarkDriver::Result] result
|
46
|
+
def report(result)
|
47
|
+
$stdout.print '.'
|
48
|
+
@job_warmup_context_result[@job][!@with_benchmark][@context] = result
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def save_record
|
54
|
+
yaml = {
|
55
|
+
'type' => 'recorded',
|
56
|
+
'job_warmup_context_result' => @job_warmup_context_result,
|
57
|
+
'metrics' => @metrics,
|
58
|
+
}.to_yaml
|
59
|
+
File.write('benchmark_driver.record.yml', yaml)
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
class BenchmarkDriver::Output::Simple
|
2
|
+
NAME_LENGTH = 10
|
3
|
+
|
4
|
+
# @param [Array<BenchmarkDriver::Metric>] metrics
|
5
|
+
# @param [Array<BenchmarkDriver::Job>] jobs
|
6
|
+
# @param [Array<BenchmarkDriver::Context>] contexts
|
7
|
+
def initialize(metrics:, jobs:, contexts:)
|
8
|
+
@metrics = metrics
|
9
|
+
@context_names = contexts.map(&:name)
|
10
|
+
@name_length = jobs.map(&:name).map(&:size).max
|
11
|
+
end
|
12
|
+
|
13
|
+
def with_warmup(&block)
|
14
|
+
@with_benchmark = false
|
15
|
+
without_stdout_buffering do
|
16
|
+
$stdout.print 'warming up'
|
17
|
+
block.call
|
18
|
+
end
|
19
|
+
ensure
|
20
|
+
$stdout.puts
|
21
|
+
end
|
22
|
+
|
23
|
+
def with_benchmark(&block)
|
24
|
+
@with_benchmark = true
|
25
|
+
without_stdout_buffering do
|
26
|
+
# Show header
|
27
|
+
$stdout.puts "#{@metrics.first.name} (#{@metrics.first.unit}):"
|
28
|
+
|
29
|
+
# Show executable names
|
30
|
+
if @context_names.size > 1
|
31
|
+
$stdout.print("#{' ' * @name_length} ")
|
32
|
+
@context_names.each do |context_name|
|
33
|
+
$stdout.print("%#{NAME_LENGTH}s " % context_name)
|
34
|
+
end
|
35
|
+
$stdout.puts
|
36
|
+
end
|
37
|
+
|
38
|
+
block.call
|
39
|
+
end
|
40
|
+
ensure
|
41
|
+
@with_benchmark = false
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param [BenchmarkDriver::Job] job
|
45
|
+
def with_job(job, &block)
|
46
|
+
if @with_benchmark
|
47
|
+
$stdout.print("%-#{@name_length}s " % job.name)
|
48
|
+
end
|
49
|
+
block.call
|
50
|
+
ensure
|
51
|
+
if @with_benchmark
|
52
|
+
$stdout.puts
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# @param [BenchmarkDriver::Context] context
|
57
|
+
def with_context(context, &block)
|
58
|
+
block.call
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param [BenchmarkDriver::Result] result
|
62
|
+
def report(result)
|
63
|
+
if @with_benchmark
|
64
|
+
$stdout.print("%#{NAME_LENGTH}s " % humanize(result.values.fetch(@metrics.first)))
|
65
|
+
else
|
66
|
+
$stdout.print '.'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# benchmark_driver ouputs logs ASAP. This enables sync flag for it.
|
73
|
+
def without_stdout_buffering
|
74
|
+
sync, $stdout.sync = $stdout.sync, true
|
75
|
+
yield
|
76
|
+
ensure
|
77
|
+
$stdout.sync = sync
|
78
|
+
end
|
79
|
+
|
80
|
+
def humanize(value)
|
81
|
+
if BenchmarkDriver::Result::ERROR.equal?(value)
|
82
|
+
return " %#{NAME_LENGTH}s" % 'ERROR'
|
83
|
+
elsif value == 0.0
|
84
|
+
return " %#{NAME_LENGTH}.3f" % 0.0
|
85
|
+
elsif value < 0
|
86
|
+
raise ArgumentError.new("Negative value: #{value.inspect}")
|
87
|
+
end
|
88
|
+
|
89
|
+
scale = (Math.log10(value) / 3).to_i
|
90
|
+
prefix = "%#{NAME_LENGTH}.3f" % (value.to_f / (1000 ** scale))
|
91
|
+
suffix =
|
92
|
+
case scale
|
93
|
+
when 1; 'k'
|
94
|
+
when 2; 'M'
|
95
|
+
when 3; 'G'
|
96
|
+
when 4; 'T'
|
97
|
+
when 5; 'Q'
|
98
|
+
else # < 1000 or > 10^15, no scale or suffix
|
99
|
+
return " #{prefix}"
|
100
|
+
end
|
101
|
+
"#{prefix}#{suffix}"
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module BenchmarkDriver
|
4
|
+
module Rbenv
|
5
|
+
# @param [String] version
|
6
|
+
def self.ruby_path(version)
|
7
|
+
path = `RBENV_VERSION='#{version}' rbenv which ruby`.rstrip
|
8
|
+
unless $?.success?
|
9
|
+
abort "Failed to execute 'rbenv which ruby'"
|
10
|
+
end
|
11
|
+
path
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param [String] full_spec - "2.5.0", "2.5.0,--jit", "JIT::2.5.0,--jit", etc.
|
15
|
+
def self.parse_spec(full_spec)
|
16
|
+
name, spec = full_spec.split('::', 2)
|
17
|
+
spec ||= name # if `::` is not given, regard whole string as spec
|
18
|
+
version, *args = spec.shellsplit
|
19
|
+
BenchmarkDriver::Config::Executable.new(
|
20
|
+
name: name,
|
21
|
+
command: [BenchmarkDriver::Rbenv.ruby_path(version), *args],
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module BenchmarkDriver
|
2
|
+
# Repeat calling block and return desired result: "best", "worst" or "average".
|
3
|
+
module Repeater
|
4
|
+
VALID_TYPES = %w[best worst average]
|
5
|
+
|
6
|
+
class << self
|
7
|
+
# `block.call` can return multiple objects, but the first one is used for sort.
|
8
|
+
# When `config.repeat_result == 'average'`, how to deal with rest objects is decided
|
9
|
+
# by `:rest_on_average` option.
|
10
|
+
def with_repeat(config:, larger_better:, rest_on_average: :first, &block)
|
11
|
+
values = config.repeat_count.times.map { block.call }
|
12
|
+
|
13
|
+
case config.repeat_result
|
14
|
+
when 'best'
|
15
|
+
best_result(values, larger_better)
|
16
|
+
when 'worst'
|
17
|
+
best_result(values, !larger_better)
|
18
|
+
when 'average'
|
19
|
+
average_result(values, rest_on_average)
|
20
|
+
else
|
21
|
+
raise "unexpected repeat_result #{config.repeat_result.inspect}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def best_result(values, larger_better)
|
28
|
+
values.sort_by do |value, *|
|
29
|
+
larger_better ? value : -value
|
30
|
+
end.last
|
31
|
+
end
|
32
|
+
|
33
|
+
def average_result(values, rest_on_average)
|
34
|
+
unless values.first.is_a?(Array)
|
35
|
+
return values.inject(&:+) / values.size.to_f
|
36
|
+
end
|
37
|
+
|
38
|
+
case rest_on_average
|
39
|
+
when :first
|
40
|
+
rest = values.first[1..-1]
|
41
|
+
[values.map { |v| v[0] }.inject(&:+) / values.size.to_f, *rest]
|
42
|
+
when :average
|
43
|
+
values.first.size.times.map do |index|
|
44
|
+
values.map { |v| v[index] }.inject(&:+) / values.first.size.to_f
|
45
|
+
end
|
46
|
+
else
|
47
|
+
raise "unexpected rest_on_average #{rest_on_average.inspect}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module BenchmarkDriver
|
2
|
+
class RubyInterface
|
3
|
+
def self.run(**args, &block)
|
4
|
+
new(**args).tap { |x| block.call(x) }.run
|
5
|
+
end
|
6
|
+
|
7
|
+
# Build jobs and run. This is NOT interface for users.
|
8
|
+
def run
|
9
|
+
unless @executables.empty?
|
10
|
+
@config.executables = @executables
|
11
|
+
end
|
12
|
+
|
13
|
+
jobs = @jobs.flat_map do |job|
|
14
|
+
BenchmarkDriver::JobParser.parse({
|
15
|
+
type: @config.runner_type,
|
16
|
+
prelude: @prelude,
|
17
|
+
loop_count: @loop_count,
|
18
|
+
}.merge!(job))
|
19
|
+
end
|
20
|
+
BenchmarkDriver::Runner.run(jobs, config: @config)
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Config APIs from here
|
25
|
+
#
|
26
|
+
|
27
|
+
# @param [String,NilClass] output
|
28
|
+
# @param [String,NilClass] runner
|
29
|
+
def initialize(output: nil, runner: nil, repeat_count: 1)
|
30
|
+
@prelude = ''
|
31
|
+
@loop_count = nil
|
32
|
+
@jobs = []
|
33
|
+
@config = BenchmarkDriver::Config.new
|
34
|
+
@config.output_type = output.to_s if output
|
35
|
+
@config.runner_type = runner.to_s if runner
|
36
|
+
@config.repeat_count = Integer(repeat_count)
|
37
|
+
@executables = []
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param [String] script
|
41
|
+
def prelude(script)
|
42
|
+
@prelude << "#{script}\n"
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param [Integer] count
|
46
|
+
def loop_count(count)
|
47
|
+
@loop_count = count
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param [String] name - Name shown on result output.
|
51
|
+
# @param [String,nil] script - Benchmarked script in String. If nil, name is considered as script too.
|
52
|
+
def report(name, script = nil)
|
53
|
+
if script.nil?
|
54
|
+
script = name
|
55
|
+
end
|
56
|
+
@jobs << { benchmark: [{ name: name, script: script }] }
|
57
|
+
end
|
58
|
+
|
59
|
+
def output(type)
|
60
|
+
@config.output_type = type
|
61
|
+
end
|
62
|
+
|
63
|
+
# Backward compatibility. This is actually default now.
|
64
|
+
def compare!
|
65
|
+
@config.output_type = 'compare'
|
66
|
+
end
|
67
|
+
|
68
|
+
def rbenv(*versions)
|
69
|
+
versions.each do |version|
|
70
|
+
@executables << BenchmarkDriver::Rbenv.parse_spec(version)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def executable(name:, command:)
|
75
|
+
raise ArgumentError, "`command' should be an Array" unless command.kind_of? Array
|
76
|
+
@executables << BenchmarkDriver::Config::Executable.new(name: name, command: command)
|
77
|
+
end
|
78
|
+
|
79
|
+
def verbose(level = 1)
|
80
|
+
@config.verbose = level
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module BenchmarkDriver
|
2
|
+
module Runner
|
3
|
+
require 'benchmark_driver/runner/command_stdout'
|
4
|
+
require 'benchmark_driver/runner/ips'
|
5
|
+
require 'benchmark_driver/runner/memory'
|
6
|
+
require 'benchmark_driver/runner/once'
|
7
|
+
require 'benchmark_driver/runner/recorded'
|
8
|
+
require 'benchmark_driver/runner/ruby_stdout'
|
9
|
+
require 'benchmark_driver/runner/time'
|
10
|
+
end
|
11
|
+
|
12
|
+
class << Runner
|
13
|
+
# Main function which is used by both CLI and `Benchmark.driver`.
|
14
|
+
# @param [Array<BenchmarkDriver::*::Job>] jobs
|
15
|
+
# @param [BenchmarkDriver::Config] config
|
16
|
+
def run(jobs, config:)
|
17
|
+
if config.verbose >= 1
|
18
|
+
config.executables.each do |exec|
|
19
|
+
$stdout.puts "#{exec.name}: #{IO.popen([*exec.command, '-v'], &:read)}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
runner_config = Config::RunnerConfig.new
|
24
|
+
runner_config.members.each do |member|
|
25
|
+
runner_config[member] = config[member]
|
26
|
+
end
|
27
|
+
|
28
|
+
jobs.group_by{ |j| j.respond_to?(:contexts) && j.contexts }.each do |contexts, contexts_jobs|
|
29
|
+
contexts_jobs.group_by(&:metrics).each do |metrics, metrics_jobs|
|
30
|
+
metrics_jobs.group_by(&:class).each do |klass, klass_jobs|
|
31
|
+
runner = runner_for(klass)
|
32
|
+
contexts = build_contexts(contexts, executables: config.executables)
|
33
|
+
output = Output.new(
|
34
|
+
type: config.output_type,
|
35
|
+
metrics: metrics,
|
36
|
+
jobs: klass_jobs.map { |job| BenchmarkDriver::Job.new(name: job.name) },
|
37
|
+
contexts: contexts,
|
38
|
+
)
|
39
|
+
with_clean_env do
|
40
|
+
runner.new(config: runner_config, output: output, contexts: contexts).run(klass_jobs)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def build_contexts(contexts, executables:)
|
50
|
+
# If contexts are not specified, just use executables as contexts.
|
51
|
+
if !contexts.is_a?(Array) || contexts.empty?
|
52
|
+
return executables.map { |exec|
|
53
|
+
BenchmarkDriver::Context.new(name: exec.name, executable: exec)
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
with_executables, without_executables = contexts.partition { |context| context.name && context.executable }
|
58
|
+
with_executables + build_contexts_with_executables(without_executables, executables)
|
59
|
+
end
|
60
|
+
|
61
|
+
def build_contexts_with_executables(contexts, executables)
|
62
|
+
# Create direct product of contexts
|
63
|
+
contexts.product(executables).map do |context, executable|
|
64
|
+
name = context.name
|
65
|
+
if name.nil?
|
66
|
+
# Use the first gem name and version by default
|
67
|
+
name = context.gems.first.join(' ')
|
68
|
+
|
69
|
+
# Append Ruby executable name if it's matrix
|
70
|
+
if executables.size > 1
|
71
|
+
name = "#{name} (#{executable.name})"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
BenchmarkDriver::Context.new(
|
76
|
+
name: name,
|
77
|
+
executable: executable,
|
78
|
+
gems: context.gems,
|
79
|
+
prelude: context.prelude,
|
80
|
+
)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Dynamically find class (BenchmarkDriver::*::JobRunner) for plugin support
|
85
|
+
# @param [Class] klass - BenchmarkDriver::*::Job
|
86
|
+
# @return [Class]
|
87
|
+
def runner_for(klass)
|
88
|
+
unless match = klass.name.match(/\ABenchmarkDriver::Runner::(?<namespace>[^:]+)::Job\z/)
|
89
|
+
raise "Unexpected job class: #{klass}"
|
90
|
+
end
|
91
|
+
BenchmarkDriver.const_get("Runner::#{match[:namespace]}", false)
|
92
|
+
end
|
93
|
+
|
94
|
+
def with_clean_env(&block)
|
95
|
+
require 'bundler'
|
96
|
+
Bundler.with_clean_env do
|
97
|
+
block.call
|
98
|
+
end
|
99
|
+
rescue LoadError
|
100
|
+
block.call
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|