3 # Copyright (C) 2011 Apple Inc. All rights reserved.
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
8 # 1. Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # 2. Redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution.
14 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 # THE POSSIBILITY OF SUCH DAMAGE.
35 $stderr.puts "It does not appear that you have the 'json' package installed. Try running 'sudo gem install json'."
41 CONFIGURATION_FLNM = ENV["HOME"]+"/.bencher"
43 unless FileTest.exist? CONFIGURATION_FLNM
44 $stderr.puts "Error: no configuration file at ~/.bencher."
45 $stderr.puts "This file should contain paths to SunSpider, V8, and Kraken, in JSON"
46 $stderr.puts "format. For example:"
47 $stderr.puts "{ \"sunSpiderPath\": \"/Volumes/Data/pizlo/OpenSource/PerformanceTests/SunSpider/tests/sunspider-1.0\", \"v8Path\": \"/Volumes/Data/pizlo/OpenSource/PerformanceTests/SunSpider/tests/v8-v6\", \"krakenPath\": \"/Volumes/Data/pizlo/kraken/kraken-e119421cb325/tests/kraken-1.1\" }"
51 CONFIGURATION = JSON.parse(File::read(CONFIGURATION_FLNM))
53 SUNSPIDER_PATH = CONFIGURATION["sunSpiderPath"]
54 V8_PATH = CONFIGURATION["v8Path"]
55 KRAKEN_PATH = CONFIGURATION["krakenPath"]
57 IBR_LOOKUP=[0.00615583, 0.0975, 0.22852, 0.341628, 0.430741, 0.500526, 0.555933,
58 0.600706, 0.637513, 0.668244, 0.694254, 0.716537, 0.735827, 0.752684,
59 0.767535, 0.780716, 0.792492, 0.803074, 0.812634, 0.821313, 0.829227,
60 0.836472, 0.843129, 0.849267, 0.854943, 0.860209, 0.865107, 0.869674,
61 0.873942, 0.877941, 0.881693, 0.885223, 0.888548, 0.891686, 0.894652,
62 0.897461, 0.900124, 0.902652, 0.905056, 0.907343, 0.909524, 0.911604,
63 0.91359, 0.91549, 0.917308, 0.919049, 0.920718, 0.92232, 0.923859, 0.925338,
64 0.926761, 0.92813, 0.929449, 0.930721, 0.931948, 0.933132, 0.934275, 0.93538,
65 0.936449, 0.937483, 0.938483, 0.939452, 0.940392, 0.941302, 0.942185,
66 0.943042, 0.943874, 0.944682, 0.945467, 0.94623, 0.946972, 0.947694,
67 0.948396, 0.94908, 0.949746, 0.950395, 0.951027, 0.951643, 0.952244,
68 0.952831, 0.953403, 0.953961, 0.954506, 0.955039, 0.955559, 0.956067,
69 0.956563, 0.957049, 0.957524, 0.957988, 0.958443, 0.958887, 0.959323,
70 0.959749, 0.960166, 0.960575, 0.960975, 0.961368, 0.961752, 0.962129,
71 0.962499, 0.962861, 0.963217, 0.963566, 0.963908, 0.964244, 0.964574,
72 0.964897, 0.965215, 0.965527, 0.965834, 0.966135, 0.966431, 0.966722,
73 0.967007, 0.967288, 0.967564, 0.967836, 0.968103, 0.968366, 0.968624,
74 0.968878, 0.969128, 0.969374, 0.969617, 0.969855, 0.97009, 0.970321,
75 0.970548, 0.970772, 0.970993, 0.97121, 0.971425, 0.971636, 0.971843,
76 0.972048, 0.97225, 0.972449, 0.972645, 0.972839, 0.973029, 0.973217,
77 0.973403, 0.973586, 0.973766, 0.973944, 0.97412, 0.974293, 0.974464,
78 0.974632, 0.974799, 0.974963, 0.975125, 0.975285, 0.975443, 0.975599,
79 0.975753, 0.975905, 0.976055, 0.976204, 0.97635, 0.976495, 0.976638,
80 0.976779, 0.976918, 0.977056, 0.977193, 0.977327, 0.97746, 0.977592,
81 0.977722, 0.97785, 0.977977, 0.978103, 0.978227, 0.978349, 0.978471,
82 0.978591, 0.978709, 0.978827, 0.978943, 0.979058, 0.979171, 0.979283,
83 0.979395, 0.979504, 0.979613, 0.979721, 0.979827, 0.979933, 0.980037,
84 0.98014, 0.980242, 0.980343, 0.980443, 0.980543, 0.980641, 0.980738,
85 0.980834, 0.980929, 0.981023, 0.981116, 0.981209, 0.9813, 0.981391, 0.981481,
86 0.981569, 0.981657, 0.981745, 0.981831, 0.981916, 0.982001, 0.982085,
87 0.982168, 0.982251, 0.982332, 0.982413, 0.982493, 0.982573, 0.982651,
88 0.982729, 0.982807, 0.982883, 0.982959, 0.983034, 0.983109, 0.983183,
89 0.983256, 0.983329, 0.983401, 0.983472, 0.983543, 0.983613, 0.983683,
90 0.983752, 0.98382, 0.983888, 0.983956, 0.984022, 0.984089, 0.984154,
91 0.984219, 0.984284, 0.984348, 0.984411, 0.984474, 0.984537, 0.984599,
92 0.98466, 0.984721, 0.984782, 0.984842, 0.984902, 0.984961, 0.985019,
93 0.985077, 0.985135, 0.985193, 0.985249, 0.985306, 0.985362, 0.985417,
94 0.985472, 0.985527, 0.985582, 0.985635, 0.985689, 0.985742, 0.985795,
95 0.985847, 0.985899, 0.985951, 0.986002, 0.986053, 0.986103, 0.986153,
96 0.986203, 0.986252, 0.986301, 0.98635, 0.986398, 0.986446, 0.986494,
97 0.986541, 0.986588, 0.986635, 0.986681, 0.986727, 0.986773, 0.986818,
98 0.986863, 0.986908, 0.986953, 0.986997, 0.987041, 0.987084, 0.987128,
99 0.987171, 0.987213, 0.987256, 0.987298, 0.98734, 0.987381, 0.987423,
100 0.987464, 0.987504, 0.987545, 0.987585, 0.987625, 0.987665, 0.987704,
101 0.987744, 0.987783, 0.987821, 0.98786, 0.987898, 0.987936, 0.987974,
102 0.988011, 0.988049, 0.988086, 0.988123, 0.988159, 0.988196, 0.988232,
103 0.988268, 0.988303, 0.988339, 0.988374, 0.988409, 0.988444, 0.988479,
104 0.988513, 0.988547, 0.988582, 0.988615, 0.988649, 0.988682, 0.988716,
105 0.988749, 0.988782, 0.988814, 0.988847, 0.988879, 0.988911, 0.988943,
106 0.988975, 0.989006, 0.989038, 0.989069, 0.9891, 0.989131, 0.989161, 0.989192,
107 0.989222, 0.989252, 0.989282, 0.989312, 0.989342, 0.989371, 0.989401,
108 0.98943, 0.989459, 0.989488, 0.989516, 0.989545, 0.989573, 0.989602, 0.98963,
109 0.989658, 0.989685, 0.989713, 0.98974, 0.989768, 0.989795, 0.989822,
110 0.989849, 0.989876, 0.989902, 0.989929, 0.989955, 0.989981, 0.990007,
111 0.990033, 0.990059, 0.990085, 0.99011, 0.990136, 0.990161, 0.990186,
112 0.990211, 0.990236, 0.990261, 0.990285, 0.99031, 0.990334, 0.990358,
113 0.990383, 0.990407, 0.99043, 0.990454, 0.990478, 0.990501, 0.990525,
114 0.990548, 0.990571, 0.990594, 0.990617, 0.99064, 0.990663, 0.990686,
115 0.990708, 0.990731, 0.990753, 0.990775, 0.990797, 0.990819, 0.990841,
116 0.990863, 0.990885, 0.990906, 0.990928, 0.990949, 0.99097, 0.990991,
117 0.991013, 0.991034, 0.991054, 0.991075, 0.991096, 0.991116, 0.991137,
118 0.991157, 0.991178, 0.991198, 0.991218, 0.991238, 0.991258, 0.991278,
119 0.991298, 0.991317, 0.991337, 0.991356, 0.991376, 0.991395, 0.991414,
120 0.991433, 0.991452, 0.991471, 0.99149, 0.991509, 0.991528, 0.991547,
121 0.991565, 0.991584, 0.991602, 0.99162, 0.991639, 0.991657, 0.991675,
122 0.991693, 0.991711, 0.991729, 0.991746, 0.991764, 0.991782, 0.991799,
123 0.991817, 0.991834, 0.991851, 0.991869, 0.991886, 0.991903, 0.99192,
124 0.991937, 0.991954, 0.991971, 0.991987, 0.992004, 0.992021, 0.992037,
125 0.992054, 0.99207, 0.992086, 0.992103, 0.992119, 0.992135, 0.992151,
126 0.992167, 0.992183, 0.992199, 0.992215, 0.99223, 0.992246, 0.992262,
127 0.992277, 0.992293, 0.992308, 0.992324, 0.992339, 0.992354, 0.992369,
128 0.992384, 0.9924, 0.992415, 0.992429, 0.992444, 0.992459, 0.992474, 0.992489,
129 0.992503, 0.992518, 0.992533, 0.992547, 0.992561, 0.992576, 0.99259,
130 0.992604, 0.992619, 0.992633, 0.992647, 0.992661, 0.992675, 0.992689,
131 0.992703, 0.992717, 0.99273, 0.992744, 0.992758, 0.992771, 0.992785,
132 0.992798, 0.992812, 0.992825, 0.992839, 0.992852, 0.992865, 0.992879,
133 0.992892, 0.992905, 0.992918, 0.992931, 0.992944, 0.992957, 0.99297,
134 0.992983, 0.992995, 0.993008, 0.993021, 0.993034, 0.993046, 0.993059,
135 0.993071, 0.993084, 0.993096, 0.993109, 0.993121, 0.993133, 0.993145,
136 0.993158, 0.99317, 0.993182, 0.993194, 0.993206, 0.993218, 0.99323, 0.993242,
137 0.993254, 0.993266, 0.993277, 0.993289, 0.993301, 0.993312, 0.993324,
138 0.993336, 0.993347, 0.993359, 0.99337, 0.993382, 0.993393, 0.993404,
139 0.993416, 0.993427, 0.993438, 0.993449, 0.99346, 0.993472, 0.993483,
140 0.993494, 0.993505, 0.993516, 0.993527, 0.993538, 0.993548, 0.993559,
141 0.99357, 0.993581, 0.993591, 0.993602, 0.993613, 0.993623, 0.993634,
142 0.993644, 0.993655, 0.993665, 0.993676, 0.993686, 0.993697, 0.993707,
143 0.993717, 0.993727, 0.993738, 0.993748, 0.993758, 0.993768, 0.993778,
144 0.993788, 0.993798, 0.993808, 0.993818, 0.993828, 0.993838, 0.993848,
145 0.993858, 0.993868, 0.993877, 0.993887, 0.993897, 0.993907, 0.993916,
146 0.993926, 0.993935, 0.993945, 0.993954, 0.993964, 0.993973, 0.993983,
147 0.993992, 0.994002, 0.994011, 0.99402, 0.99403, 0.994039, 0.994048, 0.994057,
148 0.994067, 0.994076, 0.994085, 0.994094, 0.994103, 0.994112, 0.994121,
149 0.99413, 0.994139, 0.994148, 0.994157, 0.994166, 0.994175, 0.994183,
150 0.994192, 0.994201, 0.99421, 0.994218, 0.994227, 0.994236, 0.994244,
151 0.994253, 0.994262, 0.99427, 0.994279, 0.994287, 0.994296, 0.994304,
152 0.994313, 0.994321, 0.994329, 0.994338, 0.994346, 0.994354, 0.994363,
153 0.994371, 0.994379, 0.994387, 0.994395, 0.994404, 0.994412, 0.99442,
154 0.994428, 0.994436, 0.994444, 0.994452, 0.99446, 0.994468, 0.994476,
155 0.994484, 0.994492, 0.9945, 0.994508, 0.994516, 0.994523, 0.994531, 0.994539,
156 0.994547, 0.994554, 0.994562, 0.99457, 0.994577, 0.994585, 0.994593, 0.9946,
157 0.994608, 0.994615, 0.994623, 0.994631, 0.994638, 0.994645, 0.994653,
158 0.99466, 0.994668, 0.994675, 0.994683, 0.99469, 0.994697, 0.994705, 0.994712,
159 0.994719, 0.994726, 0.994734, 0.994741, 0.994748, 0.994755, 0.994762,
160 0.994769, 0.994777, 0.994784, 0.994791, 0.994798, 0.994805, 0.994812,
161 0.994819, 0.994826, 0.994833, 0.99484, 0.994847, 0.994854, 0.99486, 0.994867,
162 0.994874, 0.994881, 0.994888, 0.994895, 0.994901, 0.994908, 0.994915,
163 0.994922, 0.994928, 0.994935, 0.994942, 0.994948, 0.994955, 0.994962,
164 0.994968, 0.994975, 0.994981, 0.994988, 0.994994, 0.995001, 0.995007,
165 0.995014, 0.99502, 0.995027, 0.995033, 0.99504, 0.995046, 0.995052, 0.995059,
166 0.995065, 0.995071, 0.995078, 0.995084, 0.99509, 0.995097, 0.995103,
167 0.995109, 0.995115, 0.995121, 0.995128, 0.995134, 0.99514, 0.995146,
168 0.995152, 0.995158, 0.995164, 0.995171, 0.995177, 0.995183, 0.995189,
169 0.995195, 0.995201, 0.995207, 0.995213, 0.995219, 0.995225, 0.995231,
170 0.995236, 0.995242, 0.995248, 0.995254, 0.99526, 0.995266, 0.995272,
171 0.995277, 0.995283, 0.995289, 0.995295, 0.995301, 0.995306, 0.995312,
172 0.995318, 0.995323, 0.995329, 0.995335, 0.99534, 0.995346, 0.995352,
173 0.995357, 0.995363, 0.995369, 0.995374, 0.99538, 0.995385, 0.995391,
174 0.995396, 0.995402, 0.995407, 0.995413, 0.995418, 0.995424, 0.995429,
175 0.995435, 0.99544, 0.995445, 0.995451, 0.995456, 0.995462, 0.995467,
176 0.995472, 0.995478, 0.995483, 0.995488, 0.995493, 0.995499, 0.995504,
177 0.995509, 0.995515, 0.99552, 0.995525, 0.99553, 0.995535, 0.995541, 0.995546,
178 0.995551, 0.995556, 0.995561, 0.995566, 0.995571, 0.995577, 0.995582,
179 0.995587, 0.995592, 0.995597, 0.995602, 0.995607, 0.995612, 0.995617,
180 0.995622, 0.995627, 0.995632, 0.995637, 0.995642, 0.995647, 0.995652,
181 0.995657, 0.995661, 0.995666, 0.995671, 0.995676, 0.995681, 0.995686,
182 0.995691, 0.995695, 0.9957, 0.995705, 0.99571, 0.995715, 0.995719, 0.995724,
183 0.995729, 0.995734, 0.995738, 0.995743, 0.995748, 0.995753, 0.995757,
184 0.995762, 0.995767, 0.995771, 0.995776, 0.995781, 0.995785, 0.99579,
185 0.995794, 0.995799, 0.995804, 0.995808, 0.995813, 0.995817, 0.995822,
186 0.995826, 0.995831, 0.995835, 0.99584, 0.995844, 0.995849, 0.995853,
187 0.995858, 0.995862, 0.995867, 0.995871, 0.995876, 0.99588, 0.995885,
188 0.995889, 0.995893, 0.995898, 0.995902, 0.995906, 0.995911, 0.995915,
189 0.99592, 0.995924, 0.995928, 0.995932, 0.995937, 0.995941, 0.995945, 0.99595,
190 0.995954, 0.995958, 0.995962, 0.995967, 0.995971, 0.995975, 0.995979,
191 0.995984, 0.995988, 0.995992, 0.995996, 0.996, 0.996004, 0.996009, 0.996013,
192 0.996017, 0.996021, 0.996025, 0.996029, 0.996033, 0.996037, 0.996041,
193 0.996046, 0.99605, 0.996054, 0.996058, 0.996062, 0.996066, 0.99607, 0.996074,
194 0.996078, 0.996082, 0.996086, 0.99609, 0.996094, 0.996098, 0.996102,
195 0.996106, 0.99611, 0.996114, 0.996117, 0.996121, 0.996125, 0.996129,
196 0.996133, 0.996137, 0.996141, 0.996145, 0.996149, 0.996152, 0.996156,
199 # Run-time configuration parameters (can be set with command-line options)
204 $includeSunSpider=true
207 $benchmarkPattern=nil
213 # Helpful functions and classes
216 puts "Use the --help option to get basic usage information."
221 puts "bencher [options] <vm1> [<vm2> ...]"
223 puts "Runs one or more JavaScript runtimes against SunSpider, V8, and/or Kraken"
224 puts "benchmarks, and reports detailed statistics. What makes bencher special is"
225 puts "that each benchmark/VM configuration is run in a single VM invocation, and"
226 puts "the invocations are run in random order. This minimizes systematics due to"
227 puts "one benchmark polluting the running time of another. The fine-grained"
228 puts "interleaving of VM invocations further minimizes systematics due to changes in"
229 puts "the performance or behavior of your machine."
231 puts "Bencher is highly configurable. You can compare as many VMs as you like. You"
232 puts "can change the amount of warm-up iterations, number of iterations executed per"
233 puts "VM invocation, and the number of VM invocations per benchmark. By default,"
234 puts "SunSpider, VM, and Kraken are all run; but you can run any combination of these"
237 puts "The <vm> should be either a path to a JavaScript runtime executable (such as"
238 puts "jsc), or a string of the form <name>:<path>, where the <path> is the path to"
239 puts "the executable and <name> is the name that you would like to give the"
240 puts "configuration for the purposeof reporting. If no name is given, a generic name"
241 puts "of the form Conf#<n> will be ascribed to the configuration automatically."
244 puts "--inner <n> Set the number of inner (per-runtime-invocation)"
245 puts " iterations. Default is #{$inner}."
246 puts "--outer <n> Set the number of runtime invocations for each benchmark."
247 puts " Default is #{$outer}."
248 puts "--warmup <n> Set the number of warm-up runs per invocation. Default"
249 puts " is #{$warmup}."
250 puts "--timing-mode Set the way that bencher measures time. Possible values"
251 puts " are 'preciseTime', 'date', and 'auto'. Default is"
252 puts " 'auto', which automatically detects the best way."
253 puts "--v8-only Only run V8."
254 puts "--sunspider-only Only run SunSpider."
255 puts "--kraken-only Only run Kraken."
256 puts "--exclude-v8 Exclude V8 (only run SunSpider and Kraken)."
257 puts "--exclude-sunspider Exclude SunSpider (only run V8 and Kraken)."
258 puts "--exclude-kraken Exclude SunSpider (only run SunSpider and V8)."
259 puts "--benchmarks Only run benchmarks matching the given regular expression."
260 puts "--keep-files Keep temporary files. Useful for debugging."
261 puts "--verbose or -v Print more stuff."
262 puts "--help or -h Display this message."
265 puts "bencher TipOfTree:/Volumes/Data/pizlo/OpenSource/WebKitBuild/Release/jsc MyChanges:/Volumes/Data/pizlo/secondary/OpenSource/WebKitBuild/Release/jsc"
270 if reason.respond_to? :backtrace
271 puts "FAILED: #{reason}"
273 puts reason.backtrace.join("\n")
275 puts "FAILED: #{reason}"
281 $stderr.puts "#{$0}: #{r1}"
286 def intArg(argName,arg,min,max)
288 unless result.to_s == arg
289 quickFail("Expected an integer value for #{argName}, but got #{arg}.",
290 "Invalid argument for command-line option")
292 if min and result<min
293 quickFail("Argument for #{argName} cannot be smaller than #{min}.",
294 "Invalid argument for command-line option")
296 if max and result>max
297 quickFail("Argument for #{argName} cannot be greater than #{max}.",
298 "Invalid argument for command-line option")
303 def computeMean(array)
312 def computeGeometricMean(array)
318 mult**(1.0/array.length)
321 def computeHarmonicMean(array)
322 1.0 / computeMean(array.collect{ | value | 1.0 / value })
325 def computeStdDev(array)
333 mean=computeMean(array)
337 sum += (value-mean)**2
339 Math.sqrt(sum/(array.length-1))
348 size.downto(1) { |n| push delete_at(rand(n)) }
353 def inverseBetaRegularized(n)
362 attr_reader :amountFaster
364 def initialize(amountFaster)
365 @amountFaster = amountFaster
373 if @amountFaster < 1.01
376 " might be #{numToStr(@amountFaster)}x faster"
382 attr_reader :amountFaster
384 def initialize(amountFaster)
385 @amountFaster = amountFaster
393 "^ definitely #{numToStr(@amountFaster)}x faster"
398 attr_reader :amountSlower
400 def initialize(amountSlower)
401 @amountSlower = amountSlower
409 "! definitely #{numToStr(@amountSlower)}x slower"
414 attr_reader :amountSlower
416 def initialize(amountSlower)
417 @amountSlower = amountSlower
425 if @amountSlower < 1.01
428 "? might be #{numToStr(@amountSlower)}x slower"
441 elsif value.respond_to? :each
489 stdDev/Math.sqrt(size)
492 # Computes a 95% Student's t distribution confidence interval
498 Math.sqrt(size-1.0)*stdErr*Math.sqrt(-1.0+1.0/inverseBetaRegularized(size-1))
511 computeGeometricMean(array)
515 computeHarmonicMean(array)
519 if upper < other.lower
520 Faster.new(other.mean/mean)
521 elsif lower > other.upper
522 Slower.new(mean/other.mean)
523 elsif mean > other.mean
524 MayBeSlower.new(mean/other.mean)
526 NoChange.new(other.mean/mean)
531 "size = #{size}, mean = #{mean}, stdDev = #{stdDev}, stdErr = #{stdErr}, confInt = #{confInt}"
535 def doublePuts(out1,out2,msg)
536 out1.puts "#{out2.path}: #{msg}" if $verbosity>=3
540 def benchRunHarness(vm, benchpath)
541 $stderr.puts "running #{benchpath} with #{vm}..." if $verbosity>=1
543 Tempfile.open("bencher") {
548 result=vm.runAndReport(file.path)
550 $stderr.puts "Could not run #{file.path}:"
551 File.open(file.path,"r") {
555 $stderr.puts "#{line}"
559 file.unlink unless $keepFiles
561 raise unless result and result.size == $inner
565 def emitTimeHelpers(file)
568 doublePuts($stderr,file,"function __bencher_curTimeMS() {")
569 doublePuts($stderr,file," return preciseTime()*1000")
570 doublePuts($stderr,file,"}")
572 doublePuts($stderr,file,"function __bencher_curTimeMS() {")
573 doublePuts($stderr,file," return Date.now()")
574 doublePuts($stderr,file,"}")
578 doublePuts($stderr,file,"function __bencher_run(__bencher_what) {")
579 doublePuts($stderr,file," var __bencher_before = __bencher_curTimeMS();")
580 doublePuts($stderr,file," run(__bencher_what);")
581 doublePuts($stderr,file," var __bencher_after = __bencher_curTimeMS();")
582 doublePuts($stderr,file," return __bencher_after - __bencher_before;")
583 doublePuts($stderr,file,"}")
586 def emitBenchRunCode(vm, file, benchpath)
587 emitTimeHelpers(file)
593 doublePuts($stderr,file,"__bencher_run(#{benchpath.inspect})")
594 doublePuts($stderr,file,"gc();")
597 doublePuts($stderr,file,"print(\"Time: \"+__bencher_run(#{benchpath.inspect}));")
598 doublePuts($stderr,file,"gc();")
601 doublePuts($stderr,file,"__bencher_count = 0;")
602 doublePuts($stderr,file,"function __bencher_doNext(result) {")
603 doublePuts($stderr,file," if (__bencher_count >= #{$warmup})")
604 doublePuts($stderr,file," debug(\"Time: \"+result);")
605 doublePuts($stderr,file," __bencher_count++;")
606 doublePuts($stderr,file," if (__bencher_count < #{$inner+$warmup})")
607 doublePuts($stderr,file," __bencher_runImpl(#{benchpath.inspect}, __bencher_doNext);")
608 doublePuts($stderr,file," else")
609 doublePuts($stderr,file," quit();")
610 doublePuts($stderr,file,"}")
611 doublePuts($stderr,file,"__bencher_runImpl(#{benchpath.inspect}, __bencher_doNext);")
616 doublePuts($stderr,file,"function runit() {")
617 File.open(benchpath,'r') {
621 doublePuts($stderr,file,line)
624 doublePuts($stderr,file,"}")
626 doublePuts($stderr,file,"runit();")
627 doublePuts($stderr,file,"gc();")
630 doublePuts($stderr,file,"before = __bencher_curTimeMS();")
631 doublePuts($stderr,file,"runit();")
632 doublePuts($stderr,file,"after = __bencher_curTimeMS();")
633 doublePuts($stderr,file,"print(\"Time: \"+(after-before));")
634 doublePuts($stderr,file,"gc();")
636 if vm.vmType == :dumpRenderTree
637 doublePuts($stderr,file,"quit();")
640 raise "bad $innerMode: #{$innerMode}"
644 def runBenchmarkAndReport(vm, benchpath)
645 benchRunHarness(vm, benchpath) {
647 emitBenchRunCode(vm, file, benchpath)
651 class StatsAccumulator
654 ($outer*$inner).times {
659 def statsForIteration(outerIteration, innerIteration)
660 @stats[outerIteration*$inner + innerIteration]
667 result.add(yield stat)
672 def geometricMeanStats
679 def arithmeticMeanStats
687 class VM < StatsAccumulator
688 def initialize(path, name, nameKind)
694 Tempfile.open("bencher-vmtest") {
696 file.puts "print(\"here\");"
706 if result.chomp == "here"
707 $stderr.puts "#{@name} is definitely a jsc-style VM." if $verbosity>=1
710 $stderr.puts "Assuming that #{@name} is a DumpRenderTree-style VM." if $verbosity>=1
711 @vmType = :dumpRenderTree
750 cmd = "#{@path} #{filename}"
751 $stderr.puts ">> #{cmd}" if $verbosity>=2
759 Tempfile.open(["bencher-htmldoc",".html"]) {
761 Tempfile.open("bencher-css") {
763 doublePuts($stderr,cssFile,".pass {\n font-weight: bold;\n color: green;\n}\n.fail {\n font-weight: bold;\n color: red;\n}\n\#console {\n white-space: pre-wrap;\n font-family: monospace;\n}")
764 Tempfile.open("bencher-pre") {
766 doublePuts($stderr,preFile,
767 "if (window.layoutTestController) {\n"+
768 " layoutTestController.dumpAsText(window.enablePixelTesting);\n"+
769 " layoutTestController.waitUntilDone();\n"+
772 "function debug(msg)\n"+
774 " var span = document.createElement(\"span\");\n"+
775 " document.getElementById(\"console\").appendChild(span); // insert it first so XHTML knows the namespace\n"+
776 " span.innerHTML = msg + '<br />';\n"+
779 "function quit() {\n"+
780 " layoutTestController.notifyDone();\n"+
783 "__bencher_continuation=null;\n"+
785 "function reportResult(result) {\n"+
786 " __bencher_continuation(result);\n"+
789 "function __bencher_runImpl(filename, continuation) {\n"+
790 " function doit() {\n"+
791 " document.getElementById(\"frameparent\").innerHTML = \"\";\n"+
792 " document.getElementById(\"frameparent\").innerHTML = \"<iframe id='testframe'>\";\n"+
793 " var testFrame = document.getElementById(\"testframe\");\n"+
794 " testFrame.contentDocument.open();\n"+
795 " testFrame.contentDocument.write(\"<!DOCTYPE html>\\n<head></head><body><div id=\\\"console\\\"></div><script type=\\\"text/javascript\\\">__bencher_before = Date.now();</script><script src=\\\"file://\"+filename+\"\\\"></script><script type=\\\"text/javascript\\\">window.parent.reportResult(Date.now() - __bencher_before);</script></body></html>\");\n"+
796 " testFrame.contentDocument.close();\n"+
798 " __bencher_continuation = continuation;\n"+
799 " window.setTimeout(doit, 10);\n"+
801 doublePuts($stderr,htmlFile,"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n<html><head><link rel=\"stylesheet\" href=\"file://#{cssFile.path}\"><script src=\"file://#{preFile.path}\"></script></head><body><div id=\"console\"></div><div id=\"frameparent\"></div><script src=\"file://#{filename}\"></script></body></html>")
807 cmd = "#{path} #{htmlFile.path}"
808 $stderr.puts ">> #{cmd}" if $verbosity>=2
817 htmlFile.unlink unless $keepFiles
818 preFile.unlink unless $keepFiles
819 cssFile.unlink unless $keepFiles
829 def runAndReport(filename)
837 $stderr.puts "stdout: #{line}" if $verbosity>=2
839 result.add($~.post_match.to_f)
843 break if result.size == $inner
847 $stderr.puts "Failed to run #{filename} on #{self}, retrying..."
849 raise "Failed to run #{filename} on #{self}" unless result and result.size == $inner
854 class SunSpiderBenchmark
855 attr_accessor :benchmarkSuite
866 runBenchmarkAndReport(vm, "#{SUNSPIDER_PATH}/#{@name}.js")
871 attr_accessor :benchmarkSuite
882 runBenchmarkAndReport(vm, "#{V8_PATH}/v8-#{@name}.js")
886 class KrakenBenchmark
887 attr_accessor :benchmarkSuite
898 # Kraken requires some special-casing
900 dataPath="#{KRAKEN_PATH}/#{@name}-data.js"
901 benchPath="#{KRAKEN_PATH}/#{@name}.js"
903 benchRunHarness(vm, benchPath) {
905 emitTimeHelpers(file)
906 doublePuts($stderr,file,"load(#{dataPath.inspect});")
907 doublePuts($stderr,file,"gc();");
908 if $innerMode == :reload
911 doublePuts($stderr,file,"for (var __bencher_index = 0; __bencher_index < #{$warmup+$inner}; ++__bencher_index) {")
912 doublePuts($stderr,file," before = __bencher_curTimeMS();")
913 doublePuts($stderr,file," load(#{benchPath.inspect});")
914 doublePuts($stderr,file," after = __bencher_curTimeMS();")
915 doublePuts($stderr,file," if (__bencher_index >= #{$warmup}) #{vm.printFunction}(\"Time: \"+(after-before));");
916 doublePuts($stderr,file," gc();");
917 doublePuts($stderr,file,"}")
919 raise "Kraken in DumpRenderTree is currently unsupported."
924 emitBenchRunCode(vm, file, benchPath)
931 def initialize(name, path)
950 if not $benchmarkPattern or "#{@name}/#{benchmark.to_s}" =~ $benchmarkPattern
951 benchmark.benchmarkSuite = self
952 @benchmarks << benchmark
965 @benchmarks.delete_if {
973 def initialize(vm, benchmark, suiteOnVM)
975 @benchmark = benchmark
976 @suiteOnVM = suiteOnVM
981 "#{@benchmark} on #{@vm}"
993 @benchmark.benchmarkSuite
1004 def runRecordAndReport
1005 result = @benchmark.runAndReport(@vm)
1011 class SuiteOnVM < StatsAccumulator
1012 def initialize(vm, suite)
1019 "#{@suite} on #{@vm}"
1032 def initialize(benchmarkOnVM, iteration)
1033 @benchmarkOnVM = benchmarkOnVM
1034 @iteration = iteration
1038 "#{@benchmarkOnVM} \##{@iteration+1}"
1046 @benchmarkOnVM.benchmark
1050 @benchmarkOnVM.suite
1062 @benchmarkOnVM.runRecordAndReport.array.each_with_index {
1064 @benchmarkOnVM.vm.statsForIteration(@iteration, index).add(value)
1065 @benchmarkOnVM.suiteOnVM.statsForIteration(@iteration, index).add(value)
1079 while str.length<chars
1085 def center(str,chars)
1086 while str.length<chars
1095 def statsToStr(stats)
1096 lpad(numToStr(stats.mean),11)+"+-"+rpad(numToStr(stats.confInt),9)
1100 GetoptLong.new(['--inner', GetoptLong::REQUIRED_ARGUMENT],
1101 ['--outer', GetoptLong::REQUIRED_ARGUMENT],
1102 ['--warmup', GetoptLong::REQUIRED_ARGUMENT],
1103 ['--time-mode', GetoptLong::REQUIRED_ARGUMENT],
1104 ['--sunspider-only', GetoptLong::NO_ARGUMENT],
1105 ['--v8-only', GetoptLong::NO_ARGUMENT],
1106 ['--kraken-only', GetoptLong::NO_ARGUMENT],
1107 ['--exclude-sunspider', GetoptLong::NO_ARGUMENT],
1108 ['--exclude-v8', GetoptLong::NO_ARGUMENT],
1109 ['--exclude-kraken', GetoptLong::NO_ARGUMENT],
1110 ['--benchmarks', GetoptLong::REQUIRED_ARGUMENT],
1111 ['--load-once', GetoptLong::NO_ARGUMENT],
1112 ['--keep-files', GetoptLong::NO_ARGUMENT],
1113 ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
1114 ['--help', '-h', GetoptLong::NO_ARGUMENT]).each {
1118 $inner = intArg(opt,arg,1,nil)
1120 $outer = intArg(opt,arg,1,nil)
1122 $warmup = intArg(opt,arg,0,nil)
1124 if arg.upcase == "PRECISETIME"
1125 $timeMode = :preciseTime
1126 elsif arg.upcase == "DATE"
1128 elsif arg.upcase == "AUTO"
1131 quickFail("Expected either 'preciseTime', 'date', or 'auto' for --time-mode, but got '#{arg}'.",
1132 "Invalid argument for command-line option")
1134 when '--sunspider-only'
1136 $includeKraken = false
1138 $includeSunSpider = false
1139 $includeKraken = false
1140 when '--kraken-only'
1141 $includeSunSpider = false
1143 when '--exclude-sunspider'
1144 $includeSunSpider = false
1147 when '--exclude-kraken'
1148 $includeKraken = false
1150 $benchmarkPattern = Regexp.new(arg)
1152 $innerMode = :loadOnce
1160 raise "bad option: #{opt}"
1165 quickFail("Please specify at least on configuraiton on the command line.",
1166 "Insufficient arguments")
1169 SUNSPIDER = BenchmarkSuite.new("SunSpider", SUNSPIDER_PATH)
1170 ["3d-cube", "3d-morph", "3d-raytrace", "access-binary-trees",
1171 "access-fannkuch", "access-nbody", "access-nsieve",
1172 "bitops-3bit-bits-in-byte", "bitops-bits-in-byte", "bitops-bitwise-and",
1173 "bitops-nsieve-bits", "controlflow-recursive", "crypto-aes",
1174 "crypto-md5", "crypto-sha1", "date-format-tofte", "date-format-xparb",
1175 "math-cordic", "math-partial-sums", "math-spectral-norm", "regexp-dna",
1176 "string-base64", "string-fasta", "string-tagcloud",
1177 "string-unpack-code", "string-validate-input"].each {
1179 SUNSPIDER.add SunSpiderBenchmark.new(name)
1182 V8 = BenchmarkSuite.new("V8", V8_PATH)
1183 ["crypto", "deltablue", "earley-boyer", "raytrace",
1184 "regexp", "richards", "splay"].each {
1186 V8.add V8Benchmark.new(name)
1189 KRAKEN = BenchmarkSuite.new("Kraken", KRAKEN_PATH)
1190 ["ai-astar", "audio-beat-detection", "audio-dft", "audio-fft",
1191 "audio-oscillator", "imaging-darkroom", "imaging-desaturate",
1192 "imaging-gaussian-blur", "json-parse-financial",
1193 "json-stringify-tinderbox", "stanford-crypto-aes",
1194 "stanford-crypto-ccm", "stanford-crypto-pbkdf2",
1195 "stanford-crypto-sha256-iterative"].each {
1197 KRAKEN.add KrakenBenchmark.new(name)
1204 if vm =~ /([a-zA-Z0-9_ ]+):/
1209 name = "Conf\##{$vms.length+1}"
1212 $stderr.puts "#{name}: #{vm}" if $verbosity >= 1
1213 $vms << VM.new(vm, name, nameKind)
1216 if $timeMode == :auto
1217 havePreciseTime = true
1220 if vm.vmType == :dumpRenderTree
1221 $stderr.puts "Warning: #{vm} does not have preciseTime() because it is DumpRenderTree; using Date.now instead."
1222 havePreciseTime = false
1224 Tempfile.open("bencher-timetest") {
1226 doublePuts($stderr,file,"#{vm.printFunction}(\"Time: \"+preciseTime());")
1233 thisVMHasPreciseTime = false
1234 if result =~ /Time: /
1235 secs = $~.post_match.to_i
1236 if (secs - Time.now.to_i).abs < 5
1237 thisVMHasPreciseTime = true
1240 unless thisVMHasPreciseTime
1241 $stderr.puts "Warning: #{vm} does not have preciseTime(); using Date.now instead."
1242 havePreciseTime = false
1244 file.unlink unless $keepFiles
1250 $timeMode = :preciseTime
1256 if $outer*$inner == 1
1257 $stderr.puts "Warning: will only collect one sample per benchmark/VM. Confidence interval calculation will fail."
1260 $stderr.puts "Using timeMode = #{$timeMode}." if $verbosity >= 1
1264 if $includeSunSpider and not SUNSPIDER.empty?
1265 $suites << SUNSPIDER
1268 if $includeV8 and not V8.empty?
1272 if $includeKraken and not KRAKEN.empty?
1279 $benchmarks += suite.benchmarks
1283 $suitesOnVMsForSuite = {}
1286 $suitesOnVMsForSuite[suite] = []
1289 $benchmarksOnVMs = []
1290 $benchmarksOnVMsForBenchmark = {}
1293 $benchmarksOnVMsForBenchmark[benchmark] = []
1300 suiteOnVM = SuiteOnVM.new(vm, suite)
1301 $suitesOnVMs << suiteOnVM
1302 $suitesOnVMsForSuite[suite] << suiteOnVM
1303 suite.benchmarks.each {
1305 benchmarkOnVM = BenchmarkOnVM.new(vm, benchmark, suiteOnVM)
1306 $benchmarksOnVMs << benchmarkOnVM
1307 $benchmarksOnVMsForBenchmark[benchmark] << benchmarkOnVM
1313 $benchmarksOnVMs.each {
1317 $plans << BenchPlan.new(benchmarkOnVM, iteration)
1323 $suitepad = $suites.collect {
1328 $benchpad = ($benchmarks +
1329 ["<arithmetic>", "<geometric>", "<harmonic>"]).collect {
1334 $vmpad = $vms.collect {
1339 $plans.each_with_index {
1342 text1 = lpad(idx.to_s,$plans.size.to_s.size)+"/"+$plans.size.to_s
1343 text2 = plan.suite.to_s+"/"+plan.benchmark.to_s+"/"+plan.vm.to_s
1344 $stderr.print "\r#{text1} #{rpad(text2,$suitepad+1+$benchpad+1+$vmpad)}"
1345 $stderr.print "\r#{text1} #{text2}"
1352 $stderr.print "\r#{$plans.size}/#{$plans.size} #{' '*($suitepad+1+$benchpad+1+$vmpad)}"
1353 $stderr.puts "\r#{$plans.size}/#{$plans.size}"
1357 $benchmarksOnVMs.each {
1359 $stderr.puts "#{benchmarkOnVM}: #{benchmarkOnVM.stats}"
1364 $stderr.puts "#{vm} (arithmeticMean): #{vm.arithmeticMeanStats}"
1365 $stderr.puts "#{vm} (geometricMean): #{vm.geometricMeanStats}"
1387 "%04d%02d%02d_%02d%02d" %
1388 [ time.year, time.month, time.day,
1389 time.hour, time.min ]
1393 $stderr.puts "Generating benchmark report at #{reportName}"
1397 outp = File.open(reportName,"w")
1399 $stderr.puts "Error: could not save report to #{reportName}: #{e}"
1405 result += " " if $suites.size > 1
1406 result += rpad("", $benchpad)
1411 result += " "+NoChange.new(0).shortForm
1413 result += lpad(center($vms[index].name, 9+9+2), 11+9+2)
1417 result += center("#{$vms[-1].name} v. #{$vms[0].name}",26)
1424 $columns = createVMsString.size
1437 curLine = array.shift
1440 if (curLine + " " + curStr).size > $columns
1441 result += curLine + "\n"
1444 curLine += " " + curStr
1447 result + curLine + "\n"
1450 outp.print "Benchmark report for "
1451 if $suites.size == 1
1452 outp.print $suites[0].to_s
1453 elsif $suites.size == 2
1454 outp.print "#{$suites[0]} and #{$suites[1]}"
1456 outp.print "#{$suites[0..-2].join(', ')}, and #{$suites[-1]}"
1461 # This looks stupid; revisit later.
1465 outp.puts "#{suite} at #{suite.path}"
1471 outp.puts "VMs tested:"
1474 outp.puts "\"#{vm.name}\" at #{Pathname.new(vm.path).realpath}"
1479 outp.puts wrap("Collected #{$outer*$inner} sample#{plural($outer*$inner)} per benchmark/VM, "+
1480 "with #{$outer} VM invocation#{plural($outer)} per benchmark."+
1481 (if $warmup == 0 then (" Did not include any warm-up iterations; measurements "+
1482 "began with the very first iteration.")
1483 else (" Used #{$warmup} benchmark iteration#{plural($warmup)} per VM "+
1484 "invocation for warm-up.") end)+
1486 when :preciseTime then (" Used the jsc-specific preciseTime() function to get "+
1487 "microsecond-level timing.")
1488 when :date then (" Used the portable Date.now() method to get millisecond-"+
1492 when :reload then "" # nothing interesting to say
1493 when :loadOnce then (" Ran benchmarks using an experimental mode that "+
1494 "ensures that the benchmark code is loaded only once "+
1495 "during each VM invocation, which enables more VM "+
1498 " Reporting benchmark execution times with 95% confidence "+
1499 "intervals in milliseconds.")
1504 outp.puts createVMsString
1507 def summaryStats(outp, accumulators, name, &proc)
1508 outp.print " " if $suites.size > 1
1509 outp.print rpad(name, $benchpad)
1511 accumulators.size.times {
1514 outp.print " "+accumulators[index].stats(&proc).compareTo(accumulators[index-1].stats(&proc)).shortForm
1516 outp.print statsToStr(accumulators[index].stats(&proc))
1518 if accumulators.size>=2
1519 outp.print(" "+accumulators[-1].stats(&proc).compareTo(accumulators[0].stats(&proc)).to_s)
1524 def allSummaryStats(outp, accumulators)
1525 summaryStats(outp, accumulators, "<arithmetic>") {
1530 summaryStats(outp, accumulators, "<geometric>") {
1535 summaryStats(outp, accumulators, "<harmonic>") {
1545 outp.puts "#{suite.name}:"
1549 suite.benchmarks.each {
1551 outp.print " " if $suites.size > 1
1552 outp.print rpad(benchmark.to_s, $benchpad)
1554 myConfigs = $benchmarksOnVMsForBenchmark[benchmark]
1555 myConfigs.size.times {
1558 outp.print " "+myConfigs[index].stats.compareTo(myConfigs[index-1].stats).shortForm
1560 outp.print statsToStr(myConfigs[index].stats)
1563 outp.print(" "+myConfigs[-1].stats.compareTo(myConfigs[0].stats).to_s)
1568 allSummaryStats(outp, $suitesOnVMsForSuite[suite])
1569 outp.puts if $suites.size > 1
1574 outp.puts "All benchmarks:"
1575 allSummaryStats(outp, $vms)
1581 File.open(reportName) {