benchmark_driver 0.10.16 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/.rspec +1 -0
  3. data/.travis.yml +1 -1
  4. data/CHANGELOG.md +16 -0
  5. data/README.md +25 -9
  6. data/Rakefile +5 -48
  7. data/benchmark-driver/.gitignore +12 -0
  8. data/benchmark-driver/CODE_OF_CONDUCT.md +74 -0
  9. data/benchmark-driver/Gemfile +6 -0
  10. data/benchmark-driver/LICENSE.txt +21 -0
  11. data/benchmark-driver/README.md +8 -0
  12. data/benchmark-driver/Rakefile +1 -0
  13. data/benchmark-driver/benchmark-driver.gemspec +21 -0
  14. data/benchmark-driver/bin/console +14 -0
  15. data/benchmark-driver/bin/setup +8 -0
  16. data/benchmark-driver/lib/benchmark-driver.rb +1 -0
  17. data/benchmark-driver/lib/benchmark/driver.rb +1 -0
  18. data/benchmark_driver.gemspec +3 -1
  19. data/exe/benchmark-driver +3 -3
  20. data/lib/benchmark_driver/config.rb +3 -3
  21. data/lib/benchmark_driver/metric.rb +70 -0
  22. data/lib/benchmark_driver/output.rb +62 -8
  23. data/lib/benchmark_driver/output/compare.rb +68 -52
  24. data/lib/benchmark_driver/output/markdown.rb +21 -16
  25. data/lib/benchmark_driver/output/record.rb +26 -21
  26. data/lib/benchmark_driver/output/simple.rb +21 -16
  27. data/lib/benchmark_driver/runner.rb +5 -3
  28. data/lib/benchmark_driver/runner/command_stdout.rb +19 -19
  29. data/lib/benchmark_driver/runner/ips.rb +30 -29
  30. data/lib/benchmark_driver/runner/memory.rb +15 -16
  31. data/lib/benchmark_driver/runner/once.rb +11 -15
  32. data/lib/benchmark_driver/runner/recorded.rb +28 -21
  33. data/lib/benchmark_driver/runner/ruby_stdout.rb +157 -0
  34. data/lib/benchmark_driver/runner/time.rb +7 -10
  35. data/lib/benchmark_driver/version.rb +1 -1
  36. metadata +46 -16
  37. data/examples/exec_blank.rb +0 -13
  38. data/examples/exec_blank_simple.rb +0 -13
  39. data/examples/yaml/array_duration_time.yml +0 -2
  40. data/examples/yaml/array_loop.yml +0 -3
  41. data/examples/yaml/blank_hash.yml +0 -8
  42. data/examples/yaml/blank_hash_array.yml +0 -10
  43. data/examples/yaml/blank_loop.yml +0 -9
  44. data/examples/yaml/blank_string.yml +0 -6
  45. data/examples/yaml/blank_string_array.yml +0 -8
  46. data/examples/yaml/example_multi.yml +0 -6
  47. data/examples/yaml/example_single.yml +0 -4
  48. data/lib/benchmark_driver/metrics.rb +0 -17
@@ -1,5 +1,5 @@
1
1
  require 'benchmark_driver/struct'
2
- require 'benchmark_driver/metrics'
2
+ require 'benchmark_driver/metric'
3
3
  require 'tempfile'
4
4
  require 'shellwords'
5
5
 
@@ -8,51 +8,58 @@ class BenchmarkDriver::Runner::Recorded
8
8
  # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job"
9
9
  Job = ::BenchmarkDriver::Struct.new(
10
10
  :name, # @param [String] name - This is mandatory for all runner
11
- :job, # @param [BenchmarkDriver::Runner::*::Job]
12
- :warmup_metrics, # @param [Hash]
13
- :benchmark_metrics, # @param [Hash]
14
- :metrics_type, # @param [BenchmarkDriver::Metrics::Type]
11
+ :warmup_results, # @param [Hash{ BenchmarkDriver::Context => Array<BenchmarkDriver::Metric> } }]
12
+ :benchmark_results, # @param [Hash{ BenchmarkDriver::Context => Array<BenchmarkDriver::Metric> } }]
13
+ :metrics, # @param [Array<BenchmarkDriver::Metric>]
15
14
  )
16
15
  # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse`
17
16
  class << JobParser = Module.new
18
- # @param [Hash] metrics_by_job
19
- # @param [BenchmarkDriver::Metrics::Type] metrics_type
20
- def parse(metrics_by_job:, metrics_type:)
21
- metrics_by_job.map do |job, metrics_hash|
17
+ # @param [Hash{ String => Hash{ TrueClass,FalseClass => Hash{ BenchmarkDriver::Context => Hash{ BenchmarkDriver::Metric => Float } } } }] job_warmup_context_metric_value
18
+ # @param [BenchmarkDriver::Metrics::Type] metrics
19
+ def parse(job_warmup_context_metric_value:, metrics:)
20
+ job_warmup_context_metric_value.map do |job_name, warmup_context_values|
22
21
  Job.new(
23
- name: job.name,
24
- job: job,
25
- warmup_metrics: metrics_hash.fetch(:warmup, []),
26
- benchmark_metrics: metrics_hash.fetch(:benchmark),
27
- metrics_type: metrics_type,
22
+ name: job_name,
23
+ warmup_results: warmup_context_values.fetch(true, {}),
24
+ benchmark_results: warmup_context_values.fetch(false, {}),
25
+ metrics: metrics,
28
26
  )
29
27
  end
30
28
  end
31
29
  end
32
30
 
33
31
  # @param [BenchmarkDriver::Config::RunnerConfig] config
34
- # @param [BenchmarkDriver::Output::*] output
32
+ # @param [BenchmarkDriver::Output] output
35
33
  def initialize(config:, output:)
36
34
  @config = config
37
35
  @output = output
38
36
  end
39
37
 
40
38
  # This method is dynamically called by `BenchmarkDriver::JobRunner.run`
41
- # @param [Array<BenchmarkDriver::Default::Job>] jobs
39
+ # @param [Array<BenchmarkDriver::Runner::Recorded::Job>] record
42
40
  def run(records)
43
- @output.metrics_type = records.first.metrics_type
41
+ @output.metrics = records.first.metrics
44
42
 
45
43
  records.each do |record|
46
- unless record.warmup_metrics.empty?
44
+ unless record.warmup_results.empty?
47
45
  # TODO:
48
46
  end
49
47
  end
50
48
 
51
49
  @output.with_benchmark do
52
50
  records.each do |record|
53
- @output.with_job(record.job) do
54
- record.benchmark_metrics.each do |metrics|
55
- @output.report(metrics)
51
+ @output.with_job(name: record.name) do
52
+ record.benchmark_results.each do |context, metric_values|
53
+ @output.with_context(
54
+ name: context.name,
55
+ executable: context.executable,
56
+ duration: context.duration,
57
+ loop_count: context.loop_count,
58
+ ) do
59
+ metric_values.each do |metric, value|
60
+ @output.report(value: value, metric: metric)
61
+ end
62
+ end
56
63
  end
57
64
  end
58
65
  end
@@ -0,0 +1,157 @@
1
+ require 'benchmark_driver/struct'
2
+ require 'benchmark_driver/metric'
3
+ require 'tempfile'
4
+ require 'shellwords'
5
+ require 'open3'
6
+
7
+ # Use stdout of ruby command
8
+ class BenchmarkDriver::Runner::RubyStdout
9
+ # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job"
10
+ Job = ::BenchmarkDriver::Struct.new(
11
+ :name, # @param [String] name - This is mandatory for all runner
12
+ :command, # @param [Array<String>]
13
+ :working_directory, # @param [String,NilClass]
14
+ :metrics, # @param [Array<BenchmarkDriver::Metric>]
15
+ :value_from_stdout, # @param [String]
16
+ :environment_from_stdout # @param [Hash{ String => String }]
17
+ )
18
+ # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse`
19
+ class << JobParser = Module.new
20
+ # @param [String] name
21
+ # @param [String] command
22
+ # @param [String,NilClass] working_directory
23
+ # @param [Hash] metrics_type
24
+ # @param [String] stdout_to_metrics
25
+ def parse(name:, command:, working_directory: nil, metrics:, environment: {})
26
+ unless metrics.is_a?(Hash)
27
+ raise ArgumentError.new("metrics must be Hash, but got #{metrics.class}")
28
+ end
29
+ if metrics.size == 0
30
+ raise ArgumentError.new('At least one metric must be specified"')
31
+ elsif metrics.size != 1
32
+ raise NotImplementedError.new('Having multiple metrics is not supported yet')
33
+ end
34
+
35
+ metric, value_from_stdout = parse_metric(*metrics.first)
36
+ environment_from_stdout = Hash[environment.map { |k, v| [k, parse_environment(v)] }]
37
+
38
+ Job.new(
39
+ name: name,
40
+ command: command.shellsplit,
41
+ working_directory: working_directory,
42
+ metrics: [metric],
43
+ value_from_stdout: value_from_stdout,
44
+ environment_from_stdout: environment_from_stdout,
45
+ )
46
+ end
47
+
48
+ private
49
+
50
+ def parse_metric(name, unit:, from_stdout:, larger_better: true, worse_word: 'slower')
51
+ metric = BenchmarkDriver::Metric.new(
52
+ name: name,
53
+ unit: unit,
54
+ larger_better: larger_better,
55
+ worse_word: worse_word,
56
+ )
57
+ [metric, from_stdout]
58
+ end
59
+
60
+ def parse_environment(from_stdout:)
61
+ from_stdout
62
+ end
63
+ end
64
+
65
+ # @param [BenchmarkDriver::Config::RunnerConfig] config
66
+ # @param [BenchmarkDriver::Output] output
67
+ def initialize(config:, output:)
68
+ @config = config
69
+ @output = output
70
+ end
71
+
72
+ # This method is dynamically called by `BenchmarkDriver::JobRunner.run`
73
+ # @param [Array<BenchmarkDriver::Default::Job>] jobs
74
+ def run(jobs)
75
+ metric = jobs.first.metrics.first
76
+ @output.metrics = [metric]
77
+
78
+ @output.with_benchmark do
79
+ jobs.each do |job|
80
+ @output.with_job(name: job.name) do
81
+ @config.executables.each do |exec|
82
+ best_value, environment = with_repeat(metric) do
83
+ stdout = with_chdir(job.working_directory) do
84
+ with_ruby_prefix(exec) { execute(*exec.command, *job.command) }
85
+ end
86
+ script = StdoutToMetrics.new(
87
+ stdout: stdout,
88
+ value_from_stdout: job.value_from_stdout,
89
+ environment_from_stdout: job.environment_from_stdout,
90
+ )
91
+ [script.value, script.environment]
92
+ end
93
+
94
+ @output.with_context(name: exec.name, executable: exec, environment: environment) do
95
+ @output.report(value: best_value, metric: metric)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def with_ruby_prefix(executable, &block)
106
+ env = ENV.to_h.dup
107
+ ENV['PATH'] = "#{File.dirname(executable.command.first)}:#{ENV['PATH']}"
108
+ block.call
109
+ ensure
110
+ ENV.replace(env)
111
+ end
112
+
113
+ def with_chdir(working_directory, &block)
114
+ if working_directory
115
+ Dir.chdir(working_directory) { block.call }
116
+ else
117
+ block.call
118
+ end
119
+ end
120
+
121
+ def execute(*args)
122
+ stdout, stderr, status = Open3.capture3(*args)
123
+ unless status.success?
124
+ raise "Failed to execute: #{args.shelljoin} (status: #{$?.exitstatus}):\n[stdout]:\n#{stdout}\n[stderr]:\n#{stderr}"
125
+ end
126
+ stdout
127
+ end
128
+
129
+ # Return multiple times and return the best metrics
130
+ def with_repeat(metric, &block)
131
+ value_environments = @config.repeat_count.times.map do
132
+ block.call
133
+ end
134
+ value_environments.sort_by do |value, _|
135
+ if metric.larger_better
136
+ value
137
+ else
138
+ -value
139
+ end
140
+ end.last
141
+ end
142
+
143
+ StdoutToMetrics = ::BenchmarkDriver::Struct.new(:stdout, :value_from_stdout, :environment_from_stdout) do
144
+ def value
145
+ value = eval(value_from_stdout, binding)
146
+ end
147
+
148
+ def environment
149
+ ret = {}
150
+ environment_from_stdout.each do |name, from_stdout|
151
+ ret[name] = eval(from_stdout, binding)
152
+ end
153
+ ret
154
+ end
155
+ end
156
+ private_constant :StdoutToMetrics
157
+ end
@@ -6,18 +6,15 @@ class BenchmarkDriver::Runner::Time < BenchmarkDriver::Runner::Ips
6
6
  # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse`
7
7
  JobParser = BenchmarkDriver::DefaultJobParser.for(Job)
8
8
 
9
- METRICS_TYPE = BenchmarkDriver::Metrics::Type.new(unit: 's', larger_better: false)
9
+ METRIC = BenchmarkDriver::Metric.new(name: 'Execution time', unit: 's', larger_better: false)
10
10
 
11
- # Overriding BenchmarkDriver::Runner::Ips#set_metrics_type
12
- def set_metrics_type
13
- @output.metrics_type = METRICS_TYPE
11
+ # Overriding BenchmarkDriver::Runner::Ips#metric
12
+ def metric
13
+ METRIC
14
14
  end
15
15
 
16
- # Overriding BenchmarkDriver::Runner::Ips#build_metrics
17
- def build_metrics(duration:, executable:, loop_count:)
18
- BenchmarkDriver::Metrics.new(
19
- value: duration,
20
- executable: executable,
21
- )
16
+ # Overriding BenchmarkDriver::Runner::Ips#value_duration
17
+ def value_duration(duration:, loop_count:)
18
+ [duration, duration]
22
19
  end
23
20
  end
@@ -1,3 +1,3 @@
1
1
  module BenchmarkDriver
2
- VERSION = '0.10.16'
2
+ VERSION = '0.11.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: benchmark_driver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.16
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takashi Kokubun
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-06-26 00:00:00.000000000 Z
11
+ date: 2018-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-retry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
41
69
  description: Fully-featured accurate benchmark driver for Ruby
42
70
  email:
43
71
  - takashikkbn@gmail.com
@@ -47,26 +75,27 @@ extensions: []
47
75
  extra_rdoc_files: []
48
76
  files:
49
77
  - ".gitignore"
78
+ - ".rspec"
50
79
  - ".travis.yml"
51
80
  - CHANGELOG.md
52
81
  - Gemfile
53
82
  - LICENSE.txt
54
83
  - README.md
55
84
  - Rakefile
85
+ - benchmark-driver/.gitignore
86
+ - benchmark-driver/CODE_OF_CONDUCT.md
87
+ - benchmark-driver/Gemfile
88
+ - benchmark-driver/LICENSE.txt
89
+ - benchmark-driver/README.md
90
+ - benchmark-driver/Rakefile
91
+ - benchmark-driver/benchmark-driver.gemspec
92
+ - benchmark-driver/bin/console
93
+ - benchmark-driver/bin/setup
94
+ - benchmark-driver/lib/benchmark-driver.rb
95
+ - benchmark-driver/lib/benchmark/driver.rb
56
96
  - benchmark_driver.gemspec
57
97
  - bin/console
58
98
  - bin/setup
59
- - examples/exec_blank.rb
60
- - examples/exec_blank_simple.rb
61
- - examples/yaml/array_duration_time.yml
62
- - examples/yaml/array_loop.yml
63
- - examples/yaml/blank_hash.yml
64
- - examples/yaml/blank_hash_array.yml
65
- - examples/yaml/blank_loop.yml
66
- - examples/yaml/blank_string.yml
67
- - examples/yaml/blank_string_array.yml
68
- - examples/yaml/example_multi.yml
69
- - examples/yaml/example_single.yml
70
99
  - exe/benchmark-driver
71
100
  - images/optcarrot.png
72
101
  - lib/benchmark_driver.rb
@@ -74,7 +103,7 @@ files:
74
103
  - lib/benchmark_driver/default_job.rb
75
104
  - lib/benchmark_driver/default_job_parser.rb
76
105
  - lib/benchmark_driver/job_parser.rb
77
- - lib/benchmark_driver/metrics.rb
106
+ - lib/benchmark_driver/metric.rb
78
107
  - lib/benchmark_driver/output.rb
79
108
  - lib/benchmark_driver/output/compare.rb
80
109
  - lib/benchmark_driver/output/markdown.rb
@@ -88,10 +117,11 @@ files:
88
117
  - lib/benchmark_driver/runner/memory.rb
89
118
  - lib/benchmark_driver/runner/once.rb
90
119
  - lib/benchmark_driver/runner/recorded.rb
120
+ - lib/benchmark_driver/runner/ruby_stdout.rb
91
121
  - lib/benchmark_driver/runner/time.rb
92
122
  - lib/benchmark_driver/struct.rb
93
123
  - lib/benchmark_driver/version.rb
94
- homepage: https://github.com/k0kubun/benchmark_driver
124
+ homepage: https://github.com/benchmark-driver/benchmark-driver
95
125
  licenses:
96
126
  - MIT
97
127
  metadata: {}
@@ -111,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
141
  version: '0'
112
142
  requirements: []
113
143
  rubyforge_project:
114
- rubygems_version: 2.7.7
144
+ rubygems_version: 2.6.14.1
115
145
  signing_key:
116
146
  specification_version: 4
117
147
  summary: Fully-featured accurate benchmark driver for Ruby
@@ -1,13 +0,0 @@
1
- require 'benchmark_driver'
2
-
3
- Benchmark.driver do |x|
4
- x.prelude <<-EOS
5
- class Array
6
- alias_method :blank?, :empty?
7
- end
8
- array = []
9
- EOS
10
- x.report 'Array#empty?', %{ array.empty? }
11
- x.report 'Array#blank?', %{ array.blank? }
12
- x.output 'markdown'
13
- end
@@ -1,13 +0,0 @@
1
- require 'benchmark_driver'
2
-
3
- Benchmark.driver(output: :simple) do |x|
4
- x.prelude <<-EOS
5
- class Array
6
- alias_method :blank?, :empty?
7
- end
8
- array = []
9
- EOS
10
- x.report %{ array.empty? }
11
- x.report %{ array.blank? }
12
- x.loop_count 10000000
13
- end
@@ -1,2 +0,0 @@
1
- benchmark:
2
- bm_vm2_array: a = [1,2,3,4,5,6,7,8,9,10]
@@ -1,3 +0,0 @@
1
- loop_count: 6000000
2
- benchmark:
3
- bm_vm2_array: a = [1,2,3,4,5,6,7,8,9,10]
@@ -1,8 +0,0 @@
1
- prelude: |
2
- class Array
3
- alias_method :blank?, :empty?
4
- end
5
- array = []
6
- benchmark:
7
- empty: array.empty?
8
- blank: array.blank?
@@ -1,10 +0,0 @@
1
- prelude: |
2
- class Array
3
- alias_method :blank?, :empty?
4
- end
5
- array = []
6
- benchmark:
7
- - name: Array#empty?
8
- script: array.empty?
9
- - name: Array#blank?
10
- script: array.blank?