Skip to content
Newer
Older
100644 229 lines (199 sloc) 6.01 KB
1f1cd57 @bogdan do
authored Mar 15, 2012
1 require "yaml"
2 require "benchmark"
3 require "git"
d503fa4 @bogdan Encode with base 64
authored Mar 27, 2012
4 require "base64"
acd8d53 @bogdan Multirevisions runner wip
authored Aug 29, 2012
5 require "optparse"
2c61ee6 @bogdan Do
authored May 22, 2012
6 require "diffbench/encoder"
7 require "diffbench/bm"
1f1cd57 @bogdan do
authored Mar 15, 2012
8
9 class DiffBench
10
11 class Runner
3e442ba @bogdan Highlight improvement with yellow if +-5%
authored May 24, 2012
12 COLORS = {:red => 31, :green => 32, :yellow => 33}
2c61ee6 @bogdan Do
authored May 22, 2012
13
acd8d53 @bogdan Multirevisions runner wip
authored Aug 29, 2012
14 def initialize(*args)
15 opts = OptionParser.new do |opts|
16 opts.banner = <<-DOC
17 Usage: diffbench [options] file
18
19 When working tree is dirty default is run benchmark againts dirty tree and clean tree.
20 When working tree is clean default is run benchmark against current head and previous commit.
21 DOC
22
23 opts.on("-r", '--revision [REVISIONS]', 'Specify revisions to run benchmark (comma separated). Example: master,f9a845,v3.1.4') do |value|
034de11 @bogdan Ability to run command before benchmark
authored Sep 3, 2012
24 if tree_dirty?
25 raise Error, "Working tree is dirty."
26 end
acd8d53 @bogdan Multirevisions runner wip
authored Aug 29, 2012
27 @revisions = value.split(",")
28 end
034de11 @bogdan Ability to run command before benchmark
authored Sep 3, 2012
29 opts.on("-b", '--before [COMMAND]', 'Specify command to run before each benchmark run. e.g. bundle install') do |value|
30 @before_command = value
31 end
32
acd8d53 @bogdan Multirevisions runner wip
authored Aug 29, 2012
33
34 opts.on_tail('--help', 'Show this help') do
35 output opts
36 exit
37 end
1f1cd57 @bogdan do
authored Mar 15, 2012
38 end
acd8d53 @bogdan Multirevisions runner wip
authored Aug 29, 2012
39 opts.parse!(args)
40 @file = args.first or raise Error, 'File not specified'
1f1cd57 @bogdan do
authored Mar 15, 2012
41 end
42
43 def run
acd8d53 @bogdan Multirevisions runner wip
authored Aug 29, 2012
44 if @revisions
45 run_revisions
46 else
47 run_current_head
48 end
49 end
50
51 def run_revisions
52 branch = current_head
53
54 results = begin
55 @revisions.inject({}) do |result, revision|
56 output "Checkout to #{revision}"
57 output "Run benchmark with #{revision}"
034de11 @bogdan Ability to run command before benchmark
authored Sep 3, 2012
58 git_run("checkout '#{revision}'")
acd8d53 @bogdan Multirevisions runner wip
authored Aug 29, 2012
59 result[revision] = run_file
60 result
61 end
62 ensure
63 output "Checkout to #{branch}"
64 git_run("checkout '#{branch}'")
65 end
8545492 @bogdan cosmetic change
authored Sep 11, 2012
66 print_results(results)
67 end
68
69 def print_results(results)
acd8d53 @bogdan Multirevisions runner wip
authored Aug 29, 2012
70 output ""
034de11 @bogdan Ability to run command before benchmark
authored Sep 3, 2012
71 #TODO set caption the right way
acd8d53 @bogdan Multirevisions runner wip
authored Aug 29, 2012
72 caption = "Before patch: ".gsub(/./, " ") + Benchmark::Tms::CAPTION
73 output caption
74 tests = results.values.first.keys
75 tests.each do |test|
76 output(("-" * (caption.size - test.size)) + test)
77 results.each do |revision, benchmark|
78 output "#{revision}: #{benchmark[test].format}"
79 end
034de11 @bogdan Ability to run command before benchmark
authored Sep 3, 2012
80 #TODO set improvement
acd8d53 @bogdan Multirevisions runner wip
authored Aug 29, 2012
81 #improvement = improvement_percentage(before_patch, after_patch)
82 #color_string = result_color(improvement)
83 #output self.class.color("Improvement: #{improvement}%", color_string).strip
84 output ""
85 end
86 end
87
88 def run_current_head
eda3a41 @bogdan Centralized output
authored Apr 16, 2012
89 output "Running benchmark with current working tree"
1f1cd57 @bogdan do
authored Mar 15, 2012
90 first_run = run_file
f4451b2 @bogdan Support benchmark against HEAD^
authored Mar 15, 2012
91 if tree_dirty?
eda3a41 @bogdan Centralized output
authored Apr 16, 2012
92 output "Stashing changes"
f4451b2 @bogdan Support benchmark against HEAD^
authored Mar 15, 2012
93 git_run "stash"
eda3a41 @bogdan Centralized output
authored Apr 16, 2012
94 output "Running benchmark with clean working tree"
4beafd9 @bogdan Ensure changes are stashed back
authored Mar 15, 2012
95 begin
96 second_run = run_file
97 ensure
eda3a41 @bogdan Centralized output
authored Apr 16, 2012
98 output "Applying stashed changes back"
4beafd9 @bogdan Ensure changes are stashed back
authored Mar 15, 2012
99 git_run "stash pop"
100 end
a634e3b @bogdan Support non-brach head
authored Mar 15, 2012
101 elsif branch = current_head
eda3a41 @bogdan Centralized output
authored Apr 16, 2012
102 output "Checkout HEAD^"
f4451b2 @bogdan Support benchmark against HEAD^
authored Mar 15, 2012
103 git_run "checkout 'HEAD^'"
eda3a41 @bogdan Centralized output
authored Apr 16, 2012
104 output "Running benchmark with HEAD^"
568bb8f @bogdan Rescue code
authored Mar 15, 2012
105 begin
106 second_run = run_file
107 ensure
eda3a41 @bogdan Centralized output
authored Apr 16, 2012
108 output "Checkout to previous HEAD again"
568bb8f @bogdan Rescue code
authored Mar 15, 2012
109 git_run "checkout #{branch}"
110 end
a634e3b @bogdan Support non-brach head
authored Mar 15, 2012
111 else
112 raise Error, "No current branch."
f4451b2 @bogdan Support benchmark against HEAD^
authored Mar 15, 2012
113 end
eda3a41 @bogdan Centralized output
authored Apr 16, 2012
114 output ""
a2244b8 @bogdan Style output
authored Mar 15, 2012
115 caption = "Before patch: ".gsub(/./, " ") + Benchmark::Tms::CAPTION
eda3a41 @bogdan Centralized output
authored Apr 16, 2012
116 output caption
1f1cd57 @bogdan do
authored Mar 15, 2012
117 first_run.keys.each do |test|
84f4c6f @bogdan Fix warning
authored Aug 29, 2012
118 output(("-" * (caption.size - test.size)) + test)
f51ef12 @bogdan do
authored May 22, 2012
119 before_patch = second_run[test]
120 after_patch = first_run[test]
121 improvement = improvement_percentage(before_patch, after_patch)
122 color_string = result_color(improvement)
123 output "After patch: #{after_patch.format}"
124 output "Before patch: #{before_patch.format}"
3e442ba @bogdan Highlight improvement with yellow if +-5%
authored May 24, 2012
125 output self.class.color("Improvement: #{improvement}%", color_string).strip
eda3a41 @bogdan Centralized output
authored Apr 16, 2012
126 output ""
1f1cd57 @bogdan do
authored Mar 15, 2012
127 end
128 end
129
f51ef12 @bogdan do
authored May 22, 2012
130 def improvement_percentage(before_patch, after_patch)
131 (((before_patch.real - after_patch.real).to_f / before_patch.real) * 100).round
132 end
133
134 def self.color(text, color_string)
2c61ee6 @bogdan Do
authored May 22, 2012
135 code = COLORS[color_string]
f51ef12 @bogdan do
authored May 22, 2012
136 self.color_enabled? ? "\e[#{code}m#{text}\e[0m" : text
2c61ee6 @bogdan Do
authored May 22, 2012
137 end
138
139 def self.color_enabled?
f51ef12 @bogdan do
authored May 22, 2012
140 true
2c61ee6 @bogdan Do
authored May 22, 2012
141 end
142
eda3a41 @bogdan Centralized output
authored Apr 16, 2012
143 protected
144
f51ef12 @bogdan do
authored May 22, 2012
145 def result_color(improvement)
146 if (-5..5).include?(improvement)
3e442ba @bogdan Highlight improvement with yellow if +-5%
authored May 24, 2012
147 :yellow
2c61ee6 @bogdan Do
authored May 22, 2012
148 else
149 improvement > 0 ? :green : :red
150 end
151 end
152
a634e3b @bogdan Support non-brach head
authored Mar 15, 2012
153 def current_head
154 branch = git.current_branch.to_s
155 return branch if !(branch == "(no branch)")
156 branch = git_run("symbolic-ref HEAD").gsub(/^refs\/head\//, "")
157 return branch unless branch.empty?
158 rescue Git::GitExecuteError
159 branch = git_run("rev-parse HEAD")[0..7]
160 return branch
161 end
162
1f1cd57 @bogdan do
authored Mar 15, 2012
163 def run_file
034de11 @bogdan Ability to run command before benchmark
authored Sep 3, 2012
164 output `#{@before_command}` if @before_command
781b2bf @bogdan Bugfix lib loading
authored Mar 23, 2012
165 output = `ruby -I#{File.dirname(__FILE__)} #{@file}`
f44f61b @bogdan Bugfix output filtering
authored Mar 27, 2012
166 output.split("\n").select! do |line|
f11ccbb @bogdan Filter junk output
authored Mar 27, 2012
167 if line.start_with?("diffbench:")
168 true
169 else
eda3a41 @bogdan Centralized output
authored Apr 16, 2012
170 output line
f11ccbb @bogdan Filter junk output
authored Mar 27, 2012
171 end
172 end
96fad7b @bogdan Handle exit code
authored Mar 23, 2012
173 if $?.to_i > 0
174 raise Error, "Error exit code: #{$?.to_i}"
175 end
1f1cd57 @bogdan do
authored Mar 15, 2012
176 begin
d503fa4 @bogdan Encode with base 64
authored Mar 27, 2012
177 result = Encoder.decode(output)
a2244b8 @bogdan Style output
authored Mar 15, 2012
178 raise Error, "Can not parse result of ruby script: \n #{output}" unless result.is_a?(Hash)
f4451b2 @bogdan Support benchmark against HEAD^
authored Mar 15, 2012
179 result
1f1cd57 @bogdan do
authored Mar 15, 2012
180 rescue Psych::SyntaxError
181 raise Error, "Can not run ruby script: \n#{output}"
182 end
183 end
184
f4451b2 @bogdan Support benchmark against HEAD^
authored Mar 15, 2012
185 def git_run(command)
186 git.lib.send(:command, command)
187 end
188
189 def git
190 @git ||= Git.open(discover_git_dir)
1f1cd57 @bogdan do
authored Mar 15, 2012
191 end
192
193 def discover_git_dir
194 tokens = ENV['PWD'].split("/")
195 while tokens.any?
196 path = tokens.join("/")
197 if File.exists?(path + "/.git")
198 return path
199 end
200 tokens.pop
201 end
202 raise Error, "Git working dir not found"
203 end
f4451b2 @bogdan Support benchmark against HEAD^
authored Mar 15, 2012
204
205 def tree_dirty?
206 status = git.status
aa51c93 @bogdan Fix tree_dirty?
authored Mar 27, 2012
207 status.deleted.any? || status.changed.any?
f4451b2 @bogdan Support benchmark against HEAD^
authored Mar 15, 2012
208 end
eda3a41 @bogdan Centralized output
authored Apr 16, 2012
209
210 def output(string)
211 puts string
212 end
1f1cd57 @bogdan do
authored Mar 15, 2012
213 end
214
215 class << self
216
217 def run(*args)
218 Runner.new(*args).run
219 end
220 def bm(&block)
221 DiffBench::Bm.new(&block)
222 end
223
224 end
225
226
227 class Error < StandardError; end
228 end
Something went wrong with that request. Please try again.