bencher 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099
  1. #!/usr/bin/env ruby
  2. # Copyright (C) 2011 Apple Inc. All rights reserved.
  3. #
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions
  6. # are met:
  7. # 1. Redistributions of source code must retain the above copyright
  8. # notice, this list of conditions and the following disclaimer.
  9. # 2. Redistributions in binary form must reproduce the above copyright
  10. # notice, this list of conditions and the following disclaimer in the
  11. # documentation and/or other materials provided with the distribution.
  12. #
  13. # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
  14. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  15. # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  16. # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
  17. # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  18. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  19. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  20. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  21. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  22. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  23. # THE POSSIBILITY OF SUCH DAMAGE.
  24. require 'rubygems'
  25. require 'getoptlong'
  26. require 'pathname'
  27. require 'tempfile'
  28. require 'socket'
  29. begin
  30. require 'json'
  31. rescue LoadError => e
  32. $stderr.puts "It does not appear that you have the 'json' package installed. Try running 'sudo gem install json'."
  33. exit 1
  34. end
  35. # Configuration
  36. CONFIGURATION_FLNM = ENV["HOME"]+"/.bencher"
  37. unless FileTest.exist? CONFIGURATION_FLNM
  38. $stderr.puts "Error: no configuration file at ~/.bencher."
  39. $stderr.puts "This file should contain paths to SunSpider, V8, and Kraken, as well as a"
  40. $stderr.puts "temporary directory that bencher can use for its remote mode. It should be"
  41. $stderr.puts "formatted in JSON. For example:"
  42. $stderr.puts "{"
  43. $stderr.puts " \"sunSpiderPath\": \"/Volumes/Data/pizlo/OpenSource/PerformanceTests/SunSpider/tests/sunspider-1.0\","
  44. $stderr.puts " \"v8Path\": \"/Volumes/Data/pizlo/OpenSource/PerformanceTests/SunSpider/tests/v8-v6\","
  45. $stderr.puts " \"krakenPath\": \"/Volumes/Data/pizlo/kraken/kraken-e119421cb325/tests/kraken-1.1\","
  46. $stderr.puts " \"tempPath\": \"/Volumes/Data/pizlo/bencher/temp\""
  47. $stderr.puts "}"
  48. exit 1
  49. end
  50. CONFIGURATION = JSON.parse(File::read(CONFIGURATION_FLNM))
  51. SUNSPIDER_PATH = CONFIGURATION["sunSpiderPath"]
  52. V8_PATH = CONFIGURATION["v8Path"]
  53. KRAKEN_PATH = CONFIGURATION["krakenPath"]
  54. TEMP_PATH = CONFIGURATION["tempPath"]
  55. BENCH_DATA_PATH = TEMP_PATH + "/benchdata"
  56. IBR_LOOKUP=[0.00615583, 0.0975, 0.22852, 0.341628, 0.430741, 0.500526, 0.555933,
  57. 0.600706, 0.637513, 0.668244, 0.694254, 0.716537, 0.735827, 0.752684,
  58. 0.767535, 0.780716, 0.792492, 0.803074, 0.812634, 0.821313, 0.829227,
  59. 0.836472, 0.843129, 0.849267, 0.854943, 0.860209, 0.865107, 0.869674,
  60. 0.873942, 0.877941, 0.881693, 0.885223, 0.888548, 0.891686, 0.894652,
  61. 0.897461, 0.900124, 0.902652, 0.905056, 0.907343, 0.909524, 0.911604,
  62. 0.91359, 0.91549, 0.917308, 0.919049, 0.920718, 0.92232, 0.923859, 0.925338,
  63. 0.926761, 0.92813, 0.929449, 0.930721, 0.931948, 0.933132, 0.934275, 0.93538,
  64. 0.936449, 0.937483, 0.938483, 0.939452, 0.940392, 0.941302, 0.942185,
  65. 0.943042, 0.943874, 0.944682, 0.945467, 0.94623, 0.946972, 0.947694,
  66. 0.948396, 0.94908, 0.949746, 0.950395, 0.951027, 0.951643, 0.952244,
  67. 0.952831, 0.953403, 0.953961, 0.954506, 0.955039, 0.955559, 0.956067,
  68. 0.956563, 0.957049, 0.957524, 0.957988, 0.958443, 0.958887, 0.959323,
  69. 0.959749, 0.960166, 0.960575, 0.960975, 0.961368, 0.961752, 0.962129,
  70. 0.962499, 0.962861, 0.963217, 0.963566, 0.963908, 0.964244, 0.964574,
  71. 0.964897, 0.965215, 0.965527, 0.965834, 0.966135, 0.966431, 0.966722,
  72. 0.967007, 0.967288, 0.967564, 0.967836, 0.968103, 0.968366, 0.968624,
  73. 0.968878, 0.969128, 0.969374, 0.969617, 0.969855, 0.97009, 0.970321,
  74. 0.970548, 0.970772, 0.970993, 0.97121, 0.971425, 0.971636, 0.971843,
  75. 0.972048, 0.97225, 0.972449, 0.972645, 0.972839, 0.973029, 0.973217,
  76. 0.973403, 0.973586, 0.973766, 0.973944, 0.97412, 0.974293, 0.974464,
  77. 0.974632, 0.974799, 0.974963, 0.975125, 0.975285, 0.975443, 0.975599,
  78. 0.975753, 0.975905, 0.976055, 0.976204, 0.97635, 0.976495, 0.976638,
  79. 0.976779, 0.976918, 0.977056, 0.977193, 0.977327, 0.97746, 0.977592,
  80. 0.977722, 0.97785, 0.977977, 0.978103, 0.978227, 0.978349, 0.978471,
  81. 0.978591, 0.978709, 0.978827, 0.978943, 0.979058, 0.979171, 0.979283,
  82. 0.979395, 0.979504, 0.979613, 0.979721, 0.979827, 0.979933, 0.980037,
  83. 0.98014, 0.980242, 0.980343, 0.980443, 0.980543, 0.980641, 0.980738,
  84. 0.980834, 0.980929, 0.981023, 0.981116, 0.981209, 0.9813, 0.981391, 0.981481,
  85. 0.981569, 0.981657, 0.981745, 0.981831, 0.981916, 0.982001, 0.982085,
  86. 0.982168, 0.982251, 0.982332, 0.982413, 0.982493, 0.982573, 0.982651,
  87. 0.982729, 0.982807, 0.982883, 0.982959, 0.983034, 0.983109, 0.983183,
  88. 0.983256, 0.983329, 0.983401, 0.983472, 0.983543, 0.983613, 0.983683,
  89. 0.983752, 0.98382, 0.983888, 0.983956, 0.984022, 0.984089, 0.984154,
  90. 0.984219, 0.984284, 0.984348, 0.984411, 0.984474, 0.984537, 0.984599,
  91. 0.98466, 0.984721, 0.984782, 0.984842, 0.984902, 0.984961, 0.985019,
  92. 0.985077, 0.985135, 0.985193, 0.985249, 0.985306, 0.985362, 0.985417,
  93. 0.985472, 0.985527, 0.985582, 0.985635, 0.985689, 0.985742, 0.985795,
  94. 0.985847, 0.985899, 0.985951, 0.986002, 0.986053, 0.986103, 0.986153,
  95. 0.986203, 0.986252, 0.986301, 0.98635, 0.986398, 0.986446, 0.986494,
  96. 0.986541, 0.986588, 0.986635, 0.986681, 0.986727, 0.986773, 0.986818,
  97. 0.986863, 0.986908, 0.986953, 0.986997, 0.987041, 0.987084, 0.987128,
  98. 0.987171, 0.987213, 0.987256, 0.987298, 0.98734, 0.987381, 0.987423,
  99. 0.987464, 0.987504, 0.987545, 0.987585, 0.987625, 0.987665, 0.987704,
  100. 0.987744, 0.987783, 0.987821, 0.98786, 0.987898, 0.987936, 0.987974,
  101. 0.988011, 0.988049, 0.988086, 0.988123, 0.988159, 0.988196, 0.988232,
  102. 0.988268, 0.988303, 0.988339, 0.988374, 0.988409, 0.988444, 0.988479,
  103. 0.988513, 0.988547, 0.988582, 0.988615, 0.988649, 0.988682, 0.988716,
  104. 0.988749, 0.988782, 0.988814, 0.988847, 0.988879, 0.988911, 0.988943,
  105. 0.988975, 0.989006, 0.989038, 0.989069, 0.9891, 0.989131, 0.989161, 0.989192,
  106. 0.989222, 0.989252, 0.989282, 0.989312, 0.989342, 0.989371, 0.989401,
  107. 0.98943, 0.989459, 0.989488, 0.989516, 0.989545, 0.989573, 0.989602, 0.98963,
  108. 0.989658, 0.989685, 0.989713, 0.98974, 0.989768, 0.989795, 0.989822,
  109. 0.989849, 0.989876, 0.989902, 0.989929, 0.989955, 0.989981, 0.990007,
  110. 0.990033, 0.990059, 0.990085, 0.99011, 0.990136, 0.990161, 0.990186,
  111. 0.990211, 0.990236, 0.990261, 0.990285, 0.99031, 0.990334, 0.990358,
  112. 0.990383, 0.990407, 0.99043, 0.990454, 0.990478, 0.990501, 0.990525,
  113. 0.990548, 0.990571, 0.990594, 0.990617, 0.99064, 0.990663, 0.990686,
  114. 0.990708, 0.990731, 0.990753, 0.990775, 0.990797, 0.990819, 0.990841,
  115. 0.990863, 0.990885, 0.990906, 0.990928, 0.990949, 0.99097, 0.990991,
  116. 0.991013, 0.991034, 0.991054, 0.991075, 0.991096, 0.991116, 0.991137,
  117. 0.991157, 0.991178, 0.991198, 0.991218, 0.991238, 0.991258, 0.991278,
  118. 0.991298, 0.991317, 0.991337, 0.991356, 0.991376, 0.991395, 0.991414,
  119. 0.991433, 0.991452, 0.991471, 0.99149, 0.991509, 0.991528, 0.991547,
  120. 0.991565, 0.991584, 0.991602, 0.99162, 0.991639, 0.991657, 0.991675,
  121. 0.991693, 0.991711, 0.991729, 0.991746, 0.991764, 0.991782, 0.991799,
  122. 0.991817, 0.991834, 0.991851, 0.991869, 0.991886, 0.991903, 0.99192,
  123. 0.991937, 0.991954, 0.991971, 0.991987, 0.992004, 0.992021, 0.992037,
  124. 0.992054, 0.99207, 0.992086, 0.992103, 0.992119, 0.992135, 0.992151,
  125. 0.992167, 0.992183, 0.992199, 0.992215, 0.99223, 0.992246, 0.992262,
  126. 0.992277, 0.992293, 0.992308, 0.992324, 0.992339, 0.992354, 0.992369,
  127. 0.992384, 0.9924, 0.992415, 0.992429, 0.992444, 0.992459, 0.992474, 0.992489,
  128. 0.992503, 0.992518, 0.992533, 0.992547, 0.992561, 0.992576, 0.99259,
  129. 0.992604, 0.992619, 0.992633, 0.992647, 0.992661, 0.992675, 0.992689,
  130. 0.992703, 0.992717, 0.99273, 0.992744, 0.992758, 0.992771, 0.992785,
  131. 0.992798, 0.992812, 0.992825, 0.992839, 0.992852, 0.992865, 0.992879,
  132. 0.992892, 0.992905, 0.992918, 0.992931, 0.992944, 0.992957, 0.99297,
  133. 0.992983, 0.992995, 0.993008, 0.993021, 0.993034, 0.993046, 0.993059,
  134. 0.993071, 0.993084, 0.993096, 0.993109, 0.993121, 0.993133, 0.993145,
  135. 0.993158, 0.99317, 0.993182, 0.993194, 0.993206, 0.993218, 0.99323, 0.993242,
  136. 0.993254, 0.993266, 0.993277, 0.993289, 0.993301, 0.993312, 0.993324,
  137. 0.993336, 0.993347, 0.993359, 0.99337, 0.993382, 0.993393, 0.993404,
  138. 0.993416, 0.993427, 0.993438, 0.993449, 0.99346, 0.993472, 0.993483,
  139. 0.993494, 0.993505, 0.993516, 0.993527, 0.993538, 0.993548, 0.993559,
  140. 0.99357, 0.993581, 0.993591, 0.993602, 0.993613, 0.993623, 0.993634,
  141. 0.993644, 0.993655, 0.993665, 0.993676, 0.993686, 0.993697, 0.993707,
  142. 0.993717, 0.993727, 0.993738, 0.993748, 0.993758, 0.993768, 0.993778,
  143. 0.993788, 0.993798, 0.993808, 0.993818, 0.993828, 0.993838, 0.993848,
  144. 0.993858, 0.993868, 0.993877, 0.993887, 0.993897, 0.993907, 0.993916,
  145. 0.993926, 0.993935, 0.993945, 0.993954, 0.993964, 0.993973, 0.993983,
  146. 0.993992, 0.994002, 0.994011, 0.99402, 0.99403, 0.994039, 0.994048, 0.994057,
  147. 0.994067, 0.994076, 0.994085, 0.994094, 0.994103, 0.994112, 0.994121,
  148. 0.99413, 0.994139, 0.994148, 0.994157, 0.994166, 0.994175, 0.994183,
  149. 0.994192, 0.994201, 0.99421, 0.994218, 0.994227, 0.994236, 0.994244,
  150. 0.994253, 0.994262, 0.99427, 0.994279, 0.994287, 0.994296, 0.994304,
  151. 0.994313, 0.994321, 0.994329, 0.994338, 0.994346, 0.994354, 0.994363,
  152. 0.994371, 0.994379, 0.994387, 0.994395, 0.994404, 0.994412, 0.99442,
  153. 0.994428, 0.994436, 0.994444, 0.994452, 0.99446, 0.994468, 0.994476,
  154. 0.994484, 0.994492, 0.9945, 0.994508, 0.994516, 0.994523, 0.994531, 0.994539,
  155. 0.994547, 0.994554, 0.994562, 0.99457, 0.994577, 0.994585, 0.994593, 0.9946,
  156. 0.994608, 0.994615, 0.994623, 0.994631, 0.994638, 0.994645, 0.994653,
  157. 0.99466, 0.994668, 0.994675, 0.994683, 0.99469, 0.994697, 0.994705, 0.994712,
  158. 0.994719, 0.994726, 0.994734, 0.994741, 0.994748, 0.994755, 0.994762,
  159. 0.994769, 0.994777, 0.994784, 0.994791, 0.994798, 0.994805, 0.994812,
  160. 0.994819, 0.994826, 0.994833, 0.99484, 0.994847, 0.994854, 0.99486, 0.994867,
  161. 0.994874, 0.994881, 0.994888, 0.994895, 0.994901, 0.994908, 0.994915,
  162. 0.994922, 0.994928, 0.994935, 0.994942, 0.994948, 0.994955, 0.994962,
  163. 0.994968, 0.994975, 0.994981, 0.994988, 0.994994, 0.995001, 0.995007,
  164. 0.995014, 0.99502, 0.995027, 0.995033, 0.99504, 0.995046, 0.995052, 0.995059,
  165. 0.995065, 0.995071, 0.995078, 0.995084, 0.99509, 0.995097, 0.995103,
  166. 0.995109, 0.995115, 0.995121, 0.995128, 0.995134, 0.99514, 0.995146,
  167. 0.995152, 0.995158, 0.995164, 0.995171, 0.995177, 0.995183, 0.995189,
  168. 0.995195, 0.995201, 0.995207, 0.995213, 0.995219, 0.995225, 0.995231,
  169. 0.995236, 0.995242, 0.995248, 0.995254, 0.99526, 0.995266, 0.995272,
  170. 0.995277, 0.995283, 0.995289, 0.995295, 0.995301, 0.995306, 0.995312,
  171. 0.995318, 0.995323, 0.995329, 0.995335, 0.99534, 0.995346, 0.995352,
  172. 0.995357, 0.995363, 0.995369, 0.995374, 0.99538, 0.995385, 0.995391,
  173. 0.995396, 0.995402, 0.995407, 0.995413, 0.995418, 0.995424, 0.995429,
  174. 0.995435, 0.99544, 0.995445, 0.995451, 0.995456, 0.995462, 0.995467,
  175. 0.995472, 0.995478, 0.995483, 0.995488, 0.995493, 0.995499, 0.995504,
  176. 0.995509, 0.995515, 0.99552, 0.995525, 0.99553, 0.995535, 0.995541, 0.995546,
  177. 0.995551, 0.995556, 0.995561, 0.995566, 0.995571, 0.995577, 0.995582,
  178. 0.995587, 0.995592, 0.995597, 0.995602, 0.995607, 0.995612, 0.995617,
  179. 0.995622, 0.995627, 0.995632, 0.995637, 0.995642, 0.995647, 0.995652,
  180. 0.995657, 0.995661, 0.995666, 0.995671, 0.995676, 0.995681, 0.995686,
  181. 0.995691, 0.995695, 0.9957, 0.995705, 0.99571, 0.995715, 0.995719, 0.995724,
  182. 0.995729, 0.995734, 0.995738, 0.995743, 0.995748, 0.995753, 0.995757,
  183. 0.995762, 0.995767, 0.995771, 0.995776, 0.995781, 0.995785, 0.99579,
  184. 0.995794, 0.995799, 0.995804, 0.995808, 0.995813, 0.995817, 0.995822,
  185. 0.995826, 0.995831, 0.995835, 0.99584, 0.995844, 0.995849, 0.995853,
  186. 0.995858, 0.995862, 0.995867, 0.995871, 0.995876, 0.99588, 0.995885,
  187. 0.995889, 0.995893, 0.995898, 0.995902, 0.995906, 0.995911, 0.995915,
  188. 0.99592, 0.995924, 0.995928, 0.995932, 0.995937, 0.995941, 0.995945, 0.99595,
  189. 0.995954, 0.995958, 0.995962, 0.995967, 0.995971, 0.995975, 0.995979,
  190. 0.995984, 0.995988, 0.995992, 0.995996, 0.996, 0.996004, 0.996009, 0.996013,
  191. 0.996017, 0.996021, 0.996025, 0.996029, 0.996033, 0.996037, 0.996041,
  192. 0.996046, 0.99605, 0.996054, 0.996058, 0.996062, 0.996066, 0.99607, 0.996074,
  193. 0.996078, 0.996082, 0.996086, 0.99609, 0.996094, 0.996098, 0.996102,
  194. 0.996106, 0.99611, 0.996114, 0.996117, 0.996121, 0.996125, 0.996129,
  195. 0.996133, 0.996137, 0.996141, 0.996145, 0.996149, 0.996152, 0.996156,
  196. 0.99616, 0.996164]
  197. # Run-time configuration parameters (can be set with command-line options)
  198. $rerun=1
  199. $inner=3
  200. $warmup=1
  201. $outer=4
  202. $includeSunSpider=true
  203. $includeV8=true
  204. $includeKraken=true
  205. $measureGC=false
  206. $benchmarkPattern=nil
  207. $verbosity=0
  208. $timeMode=:preciseTime
  209. $forceVMKind=nil
  210. $brief=false
  211. $silent=false
  212. $remoteHosts=[]
  213. $alsoLocal=false
  214. $sshOptions=[]
  215. $vms = []
  216. $needToCopyVMs = false
  217. $dontCopyVMs = false
  218. $prepare = true
  219. $run = true
  220. $analyze = []
  221. # Helpful functions and classes
  222. def smallUsage
  223. puts "Use the --help option to get basic usage information."
  224. exit 1
  225. end
  226. def usage
  227. puts "bencher [options] <vm1> [<vm2> ...]"
  228. puts
  229. puts "Runs one or more JavaScript runtimes against SunSpider, V8, and/or Kraken"
  230. puts "benchmarks, and reports detailed statistics. What makes bencher special is"
  231. puts "that each benchmark/VM configuration is run in a single VM invocation, and"
  232. puts "the invocations are run in random order. This minimizes systematics due to"
  233. puts "one benchmark polluting the running time of another. The fine-grained"
  234. puts "interleaving of VM invocations further minimizes systematics due to changes in"
  235. puts "the performance or behavior of your machine."
  236. puts
  237. puts "Bencher is highly configurable. You can compare as many VMs as you like. You"
  238. puts "can change the amount of warm-up iterations, number of iterations executed per"
  239. puts "VM invocation, and the number of VM invocations per benchmark. By default,"
  240. puts "SunSpider, VM, and Kraken are all run; but you can run any combination of these"
  241. puts "suites."
  242. puts
  243. puts "The <vm> should be either a path to a JavaScript runtime executable (such as"
  244. puts "jsc), or a string of the form <name>:<path>, where the <path> is the path to"
  245. puts "the executable and <name> is the name that you would like to give the"
  246. puts "configuration for the purposeof reporting. If no name is given, a generic name"
  247. puts "of the form Conf#<n> will be ascribed to the configuration automatically."
  248. puts
  249. puts "Options:"
  250. puts "--rerun <n> Set the number of iterations of the benchmark that"
  251. puts " contribute to the measured run time. Default is #{$rerun}."
  252. puts "--inner <n> Set the number of inner (per-runtime-invocation)"
  253. puts " iterations. Default is #{$inner}."
  254. puts "--outer <n> Set the number of runtime invocations for each benchmark."
  255. puts " Default is #{$outer}."
  256. puts "--warmup <n> Set the number of warm-up runs per invocation. Default"
  257. puts " is #{$warmup}."
  258. puts "--timing-mode Set the way that bencher measures time. Possible values"
  259. puts " are 'preciseTime' and 'date'. Default is 'preciseTime'."
  260. puts "--force-vm-kind Turn off auto-detection of VM kind, and assume that it is"
  261. puts " the one specified. Valid arguments are 'jsc' or"
  262. puts " 'DumpRenderTree'."
  263. puts "--force-vm-copy Force VM builds to be copied to bencher's working directory."
  264. puts " This may reduce pathologies resulting from path names."
  265. puts "--dont-copy-vms Don't copy VMs even when doing a remote benchmarking run;"
  266. puts " instead assume that they are already there."
  267. puts "--v8-only Only run V8."
  268. puts "--sunspider-only Only run SunSpider."
  269. puts "--kraken-only Only run Kraken."
  270. puts "--exclude-v8 Exclude V8 (only run SunSpider and Kraken)."
  271. puts "--exclude-sunspider Exclude SunSpider (only run V8 and Kraken)."
  272. puts "--exclude-kraken Exclude Kraken (only run SunSpider and V8)."
  273. puts "--benchmarks Only run benchmarks matching the given regular expression."
  274. puts "--measure-gc Turn off manual calls to gc(), so that GC time is measured."
  275. puts " Works best with large values of --inner. You can also say"
  276. puts " --measure-gc <conf>, which turns this on for one"
  277. puts " configuration only."
  278. puts "--verbose or -v Print more stuff."
  279. puts "--brief Print only the final result for each VM."
  280. puts "--silent Don't print progress. This might slightly reduce some"
  281. puts " performance perturbation."
  282. puts "--remote <sshhosts> Performance performance measurements remotely, on the given"
  283. puts " SSH host(s). Easiest way to use this is to specify the SSH"
  284. puts " user@host string. However, you can also supply a comma-"
  285. puts " separated list of SSH hosts. Alternatively, you can use this"
  286. puts " option multiple times to specify multiple hosts. This"
  287. puts " automatically copies the WebKit release builds of the VMs"
  288. puts " you specified to all of the hosts."
  289. puts "--ssh-options Pass additional options to SSH."
  290. puts "--local Also do a local benchmark run even when doing --remote."
  291. puts "--prepare-only Only prepare the bencher runscript (a shell script that"
  292. puts " invokes the VMs to run benchmarks) but don't run it."
  293. puts "--analyze Only read the output of the runscript but don't do anything"
  294. puts " else. This requires passing the same arguments to bencher"
  295. puts " that you passed when running --prepare-only."
  296. puts "--help or -h Display this message."
  297. puts
  298. puts "Example:"
  299. puts "bencher TipOfTree:/Volumes/Data/pizlo/OpenSource/WebKitBuild/Release/jsc MyChanges:/Volumes/Data/pizlo/secondary/OpenSource/WebKitBuild/Release/jsc"
  300. exit 1
  301. end
  302. def fail(reason)
  303. if reason.respond_to? :backtrace
  304. puts "FAILED: #{reason}"
  305. puts "Stack trace:"
  306. puts reason.backtrace.join("\n")
  307. else
  308. puts "FAILED: #{reason}"
  309. end
  310. smallUsage
  311. end
  312. def quickFail(r1,r2)
  313. $stderr.puts "#{$0}: #{r1}"
  314. puts
  315. fail(r2)
  316. end
  317. def intArg(argName,arg,min,max)
  318. result=arg.to_i
  319. unless result.to_s == arg
  320. quickFail("Expected an integer value for #{argName}, but got #{arg}.",
  321. "Invalid argument for command-line option")
  322. end
  323. if min and result<min
  324. quickFail("Argument for #{argName} cannot be smaller than #{min}.",
  325. "Invalid argument for command-line option")
  326. end
  327. if max and result>max
  328. quickFail("Argument for #{argName} cannot be greater than #{max}.",
  329. "Invalid argument for command-line option")
  330. end
  331. result
  332. end
  333. def computeMean(array)
  334. sum=0.0
  335. array.each {
  336. | value |
  337. sum += value
  338. }
  339. sum/array.length
  340. end
  341. def computeGeometricMean(array)
  342. mult=1.0
  343. array.each {
  344. | value |
  345. mult*=value
  346. }
  347. mult**(1.0/array.length)
  348. end
  349. def computeHarmonicMean(array)
  350. 1.0 / computeMean(array.collect{ | value | 1.0 / value })
  351. end
  352. def computeStdDev(array)
  353. case array.length
  354. when 0
  355. 0.0/0.0
  356. when 1
  357. 0.0
  358. else
  359. begin
  360. mean=computeMean(array)
  361. sum=0.0
  362. array.each {
  363. | value |
  364. sum += (value-mean)**2
  365. }
  366. Math.sqrt(sum/(array.length-1))
  367. rescue
  368. 0.0/0.0
  369. end
  370. end
  371. end
  372. class Array
  373. def shuffle!
  374. size.downto(1) { |n| push delete_at(rand(n)) }
  375. self
  376. end
  377. end
  378. def inverseBetaRegularized(n)
  379. IBR_LOOKUP[n-1]
  380. end
  381. def numToStr(num)
  382. "%.4f"%(num.to_f)
  383. end
  384. class NoChange
  385. attr_reader :amountFaster
  386. def initialize(amountFaster)
  387. @amountFaster = amountFaster
  388. end
  389. def shortForm
  390. " "
  391. end
  392. def longForm
  393. " might be #{numToStr(@amountFaster)}x faster"
  394. end
  395. def to_s
  396. if @amountFaster < 1.01
  397. ""
  398. else
  399. longForm
  400. end
  401. end
  402. end
  403. class Faster
  404. attr_reader :amountFaster
  405. def initialize(amountFaster)
  406. @amountFaster = amountFaster
  407. end
  408. def shortForm
  409. "^"
  410. end
  411. def longForm
  412. "^ definitely #{numToStr(@amountFaster)}x faster"
  413. end
  414. def to_s
  415. longForm
  416. end
  417. end
  418. class Slower
  419. attr_reader :amountSlower
  420. def initialize(amountSlower)
  421. @amountSlower = amountSlower
  422. end
  423. def shortForm
  424. "!"
  425. end
  426. def longForm
  427. "! definitely #{numToStr(@amountSlower)}x slower"
  428. end
  429. def to_s
  430. longForm
  431. end
  432. end
  433. class MayBeSlower
  434. attr_reader :amountSlower
  435. def initialize(amountSlower)
  436. @amountSlower = amountSlower
  437. end
  438. def shortForm
  439. "?"
  440. end
  441. def longForm
  442. "? might be #{numToStr(@amountSlower)}x slower"
  443. end
  444. def to_s
  445. if @amountSlower < 1.01
  446. "?"
  447. else
  448. longForm
  449. end
  450. end
  451. end
  452. class Stats
  453. def initialize
  454. @array = []
  455. end
  456. def add(value)
  457. if value.is_a? Stats
  458. add(value.array)
  459. elsif value.respond_to? :each
  460. value.each {
  461. | v |
  462. add(v)
  463. }
  464. else
  465. @array << value.to_f
  466. end
  467. end
  468. def array
  469. @array
  470. end
  471. def sum
  472. result=0
  473. @array.each {
  474. | value |
  475. result += value
  476. }
  477. result
  478. end
  479. def min
  480. @array.min
  481. end
  482. def max
  483. @array.max
  484. end
  485. def size
  486. @array.length
  487. end
  488. def mean
  489. computeMean(array)
  490. end
  491. def arithmeticMean
  492. mean
  493. end
  494. def stdDev
  495. computeStdDev(array)
  496. end
  497. def stdErr
  498. stdDev/Math.sqrt(size)
  499. end
  500. # Computes a 95% Student's t distribution confidence interval
  501. def confInt
  502. if size < 2
  503. 0.0/0.0
  504. else
  505. raise if size > 1000
  506. Math.sqrt(size-1.0)*stdErr*Math.sqrt(-1.0+1.0/inverseBetaRegularized(size-1))
  507. end
  508. end
  509. def lower
  510. mean-confInt
  511. end
  512. def upper
  513. mean+confInt
  514. end
  515. def geometricMean
  516. computeGeometricMean(array)
  517. end
  518. def harmonicMean
  519. computeHarmonicMean(array)
  520. end
  521. def compareTo(other)
  522. if upper < other.lower
  523. Faster.new(other.mean/mean)
  524. elsif lower > other.upper
  525. Slower.new(mean/other.mean)
  526. elsif mean > other.mean
  527. MayBeSlower.new(mean/other.mean)
  528. else
  529. NoChange.new(other.mean/mean)
  530. end
  531. end
  532. def to_s
  533. "size = #{size}, mean = #{mean}, stdDev = #{stdDev}, stdErr = #{stdErr}, confInt = #{confInt}"
  534. end
  535. end
  536. def doublePuts(out1,out2,msg)
  537. out1.puts "#{out2.path}: #{msg}" if $verbosity>=3
  538. out2.puts msg
  539. end
  540. class Benchfile < File
  541. @@counter = 0
  542. attr_reader :filename, :basename
  543. def initialize(name)
  544. @basename, @filename = Benchfile.uniqueFilename(name)
  545. super(@filename, "w")
  546. end
  547. def self.uniqueFilename(name)
  548. if name.is_a? Array
  549. basename = name[0] + @@counter.to_s + name[1]
  550. else
  551. basename = name + @@counter.to_s
  552. end
  553. filename = BENCH_DATA_PATH + "/" + basename
  554. @@counter += 1
  555. raise "Benchfile #{filename} already exists" if FileTest.exist?(filename)
  556. [basename, filename]
  557. end
  558. def self.create(name)
  559. file = Benchfile.new(name)
  560. yield file
  561. file.close
  562. file.basename
  563. end
  564. end
  565. $dataFiles={}
  566. def ensureFile(key, filename)
  567. unless $dataFiles[key]
  568. $dataFiles[key] = Benchfile.create(key) {
  569. | outp |
  570. doublePuts($stderr,outp,IO::read(filename))
  571. }
  572. end
  573. $dataFiles[key]
  574. end
  575. def emitBenchRunCodeFile(name, plan, benchDataPath, benchPath)
  576. case plan.vm.vmType
  577. when :jsc
  578. Benchfile.create("bencher") {
  579. | file |
  580. case $timeMode
  581. when :preciseTime
  582. doublePuts($stderr,file,"function __bencher_curTimeMS() {")
  583. doublePuts($stderr,file," return preciseTime()*1000")
  584. doublePuts($stderr,file,"}")
  585. when :date
  586. doublePuts($stderr,file,"function __bencher_curTimeMS() {")
  587. doublePuts($stderr,file," return Date.now()")
  588. doublePuts($stderr,file,"}")
  589. else
  590. raise
  591. end
  592. if benchDataPath
  593. doublePuts($stderr,file,"load(#{benchDataPath.inspect});")
  594. doublePuts($stderr,file,"gc();")
  595. doublePuts($stderr,file,"for (var __bencher_index = 0; __bencher_index < #{$warmup+$inner}; ++__bencher_index) {")
  596. doublePuts($stderr,file," before = __bencher_curTimeMS();")
  597. $rerun.times {
  598. doublePuts($stderr,file," load(#{benchPath.inspect});")
  599. }
  600. doublePuts($stderr,file," after = __bencher_curTimeMS();")
  601. doublePuts($stderr,file," if (__bencher_index >= #{$warmup}) print(\"#{name}: #{plan.vm}: #{plan.iteration}: \" + (__bencher_index - #{$warmup}) + \": Time: \"+(after-before));");
  602. doublePuts($stderr,file," gc();") unless plan.vm.shouldMeasureGC
  603. doublePuts($stderr,file,"}")
  604. else
  605. doublePuts($stderr,file,"function __bencher_run(__bencher_what) {")
  606. doublePuts($stderr,file," var __bencher_before = __bencher_curTimeMS();")
  607. $rerun.times {
  608. doublePuts($stderr,file," run(__bencher_what);")
  609. }
  610. doublePuts($stderr,file," var __bencher_after = __bencher_curTimeMS();")
  611. doublePuts($stderr,file," return __bencher_after - __bencher_before;")
  612. doublePuts($stderr,file,"}")
  613. $warmup.times {
  614. doublePuts($stderr,file,"__bencher_run(#{benchPath.inspect})")
  615. doublePuts($stderr,file,"gc();") unless plan.vm.shouldMeasureGC
  616. }
  617. $inner.times {
  618. | innerIndex |
  619. doublePuts($stderr,file,"print(\"#{name}: #{plan.vm}: #{plan.iteration}: #{innerIndex}: Time: \"+__bencher_run(#{benchPath.inspect}));")
  620. doublePuts($stderr,file,"gc();") unless plan.vm.shouldMeasureGC
  621. }
  622. end
  623. }
  624. when :dumpRenderTree
  625. mainCode = Benchfile.create("bencher") {
  626. | file |
  627. doublePuts($stderr,file,"__bencher_count = 0;")
  628. doublePuts($stderr,file,"function __bencher_doNext(result) {")
  629. doublePuts($stderr,file," if (__bencher_count >= #{$warmup})")
  630. doublePuts($stderr,file," debug(\"#{name}: #{plan.vm}: #{plan.iteration}: \" + (__bencher_count - #{$warmup}) + \": Time: \" + result);")
  631. doublePuts($stderr,file," __bencher_count++;")
  632. doublePuts($stderr,file," if (__bencher_count < #{$inner+$warmup})")
  633. doublePuts($stderr,file," __bencher_runImpl(__bencher_doNext);")
  634. doublePuts($stderr,file," else")
  635. doublePuts($stderr,file," quit();")
  636. doublePuts($stderr,file,"}")
  637. doublePuts($stderr,file,"__bencher_runImpl(__bencher_doNext);")
  638. }
  639. cssCode = Benchfile.create("bencher-css") {
  640. | file |
  641. doublePuts($stderr,file,".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}")
  642. }
  643. preCode = Benchfile.create("bencher-pre") {
  644. | file |
  645. doublePuts($stderr,file,"if (window.testRunner) {")
  646. doublePuts($stderr,file," testRunner.dumpAsText(window.enablePixelTesting);")
  647. doublePuts($stderr,file," testRunner.waitUntilDone();")
  648. doublePuts($stderr,file,"}")
  649. doublePuts($stderr,file,"")
  650. doublePuts($stderr,file,"function debug(msg)")
  651. doublePuts($stderr,file,"{")
  652. doublePuts($stderr,file," var span = document.createElement(\"span\");")
  653. doublePuts($stderr,file," document.getElementById(\"console\").appendChild(span); // insert it first so XHTML knows the namespace")
  654. doublePuts($stderr,file," span.innerHTML = msg + '<br />';")
  655. doublePuts($stderr,file,"}")
  656. doublePuts($stderr,file,"")
  657. doublePuts($stderr,file,"function quit() {")
  658. doublePuts($stderr,file," testRunner.notifyDone();")
  659. doublePuts($stderr,file,"}")
  660. doublePuts($stderr,file,"")
  661. doublePuts($stderr,file,"__bencher_continuation=null;")
  662. doublePuts($stderr,file,"")
  663. doublePuts($stderr,file,"function reportResult(result) {")
  664. doublePuts($stderr,file," __bencher_continuation(result);")
  665. doublePuts($stderr,file,"}")
  666. doublePuts($stderr,file,"")
  667. doublePuts($stderr,file,"function __bencher_runImpl(continuation) {")
  668. doublePuts($stderr,file," function doit() {")
  669. doublePuts($stderr,file," document.getElementById(\"frameparent\").innerHTML = \"\";")
  670. doublePuts($stderr,file," document.getElementById(\"frameparent\").innerHTML = \"<iframe id='testframe'>\";")
  671. doublePuts($stderr,file," var testFrame = document.getElementById(\"testframe\");")
  672. doublePuts($stderr,file," testFrame.contentDocument.open();")
  673. doublePuts($stderr,file," testFrame.contentDocument.write(\"<!DOCTYPE html>\\n<head></head><body><div id=\\\"console\\\"></div>\");")
  674. if benchDataPath
  675. doublePuts($stderr,file," testFrame.contentDocument.write(\"<script src=\\\"#{benchDataPath}\\\"></script>\");")
  676. end
  677. doublePuts($stderr,file," testFrame.contentDocument.write(\"<script type=\\\"text/javascript\\\">__bencher_before = Date.now();</script><script src=\\\"#{benchPath}\\\"></script><script type=\\\"text/javascript\\\">window.parent.reportResult(Date.now() - __bencher_before);</script></body></html>\");")
  678. doublePuts($stderr,file," testFrame.contentDocument.close();")
  679. doublePuts($stderr,file," }")
  680. doublePuts($stderr,file," __bencher_continuation = continuation;")
  681. doublePuts($stderr,file," window.setTimeout(doit, 10);")
  682. doublePuts($stderr,file,"}")
  683. }
  684. Benchfile.create(["bencher-htmldoc",".html"]) {
  685. | file |
  686. doublePuts($stderr,file,"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n<html><head><link rel=\"stylesheet\" href=\"#{cssCode}\"><script src=\"#{preCode}\"></script></head><body><div id=\"console\"></div><div id=\"frameparent\"></div><script src=\"#{mainCode}\"></script></body></html>")
  687. }
  688. else
  689. raise
  690. end
  691. end
  692. def emitBenchRunCode(name, plan, benchDataPath, benchPath)
  693. plan.vm.emitRunCode(emitBenchRunCodeFile(name, plan, benchDataPath, benchPath))
  694. end
  695. def planForDescription(plans, benchFullname, vmName, iteration)
  696. raise unless benchFullname =~ /\//
  697. suiteName = $~.pre_match
  698. benchName = $~.post_match
  699. result = plans.select{|v| v.suite.name == suiteName and v.benchmark.name == benchName and v.vm.name == vmName and v.iteration == iteration}
  700. raise unless result.size == 1
  701. result[0]
  702. end
  703. class ParsedResult
  704. attr_reader :plan, :innerIndex, :time
  705. def initialize(plan, innerIndex, time)
  706. @plan = plan
  707. @innerIndex = innerIndex
  708. @time = time
  709. raise unless @plan.is_a? BenchPlan
  710. raise unless @innerIndex.is_a? Integer
  711. raise unless @time.is_a? Numeric
  712. end
  713. def benchmark
  714. plan.benchmark
  715. end
  716. def suite
  717. plan.suite
  718. end
  719. def vm
  720. plan.vm
  721. end
  722. def outerIndex
  723. plan.iteration
  724. end
  725. def self.parse(plans, string)
  726. if string =~ /([a-zA-Z0-9\/-]+): ([a-zA-Z0-9_# ]+): ([0-9]+): ([0-9]+): Time: /
  727. benchFullname = $1
  728. vmName = $2
  729. outerIndex = $3.to_i
  730. innerIndex = $4.to_i
  731. time = $~.post_match.to_f
  732. ParsedResult.new(planForDescription(plans, benchFullname, vmName, outerIndex), innerIndex, time)
  733. else
  734. nil
  735. end
  736. end
  737. end
  738. class VM
  739. def initialize(origPath, name, nameKind, svnRevision)
  740. @origPath = origPath.to_s
  741. @path = origPath.to_s
  742. @name = name
  743. @nameKind = nameKind
  744. if $forceVMKind
  745. @vmType = $forceVMKind
  746. else
  747. if @origPath =~ /DumpRenderTree$/
  748. @vmType = :dumpRenderTree
  749. else
  750. @vmType = :jsc
  751. end
  752. end
  753. @svnRevision = svnRevision
  754. # Try to detect information about the VM.
  755. if path =~ /\/WebKitBuild\/Release\/([a-zA-Z]+)$/
  756. @checkoutPath = $~.pre_match
  757. # FIXME: Use some variant of this:
  758. # <bdash> def retrieve_revision
  759. # <bdash> `perl -I#{@path}/Tools/Scripts -MVCSUtils -e 'print svnRevisionForDirectory("#{@path}");'`.to_i
  760. # <bdash> end
  761. unless @svnRevision
  762. begin
  763. Dir.chdir(@checkoutPath) {
  764. $stderr.puts ">> cd #{@checkoutPath} && svn info" if $verbosity>=2
  765. IO.popen("svn info", "r") {
  766. | inp |
  767. inp.each_line {
  768. | line |
  769. if line =~ /Revision: ([0-9]+)/
  770. @svnRevision = $1
  771. end
  772. }
  773. }
  774. }
  775. unless @svnRevision
  776. $stderr.puts "Warning: running svn info for #{name} silently failed."
  777. end
  778. rescue => e
  779. # Failed to detect svn revision.
  780. $stderr.puts "Warning: could not get svn revision information for #{name}: #{e}"
  781. end
  782. end
  783. else
  784. $stderr.puts "Warning: could not identify checkout location for #{name}"
  785. end
  786. if @path =~ /\/Release\/([a-zA-Z]+)$/
  787. @libPath, @relativeBinPath = $~.pre_match+"/Release", "./#{$1}"
  788. elsif @path =~ /\/Contents\/Resources\/([a-zA-Z]+)$/
  789. @libPath = $~.pre_match
  790. elsif @path =~ /\/JavaScriptCore.framework\/Resources\/([a-zA-Z]+)$/
  791. @libPath, @relativeBinPath = $~.pre_match, $&[1..-1]
  792. end
  793. end
  794. def canCopyIntoBenchPath
  795. if @libPath and @relativeBinPath
  796. true
  797. else
  798. false
  799. end
  800. end
  801. def copyIntoBenchPath
  802. raise unless canCopyIntoBenchPath
  803. basename, filename = Benchfile.uniqueFilename("vm")
  804. raise unless Dir.mkdir(filename)
  805. cmd = "cp -a #{@libPath.inspect}/* #{filename.inspect}"
  806. $stderr.puts ">> #{cmd}" if $verbosity>=2
  807. raise unless system(cmd)
  808. @path = "#{basename}/#{@relativeBinPath}"
  809. @libPath = basename
  810. end
  811. def to_s
  812. @name
  813. end
  814. def name
  815. @name
  816. end
  817. def shouldMeasureGC
  818. $measureGC == true or ($measureGC == name)
  819. end
  820. def origPath
  821. @origPath
  822. end
  823. def path
  824. @path
  825. end
  826. def nameKind
  827. @nameKind
  828. end
  829. def vmType
  830. @vmType
  831. end
  832. def checkoutPath
  833. @checkoutPath
  834. end
  835. def svnRevision
  836. @svnRevision
  837. end
  838. def printFunction
  839. case @vmType
  840. when :jsc
  841. "print"
  842. when :dumpRenderTree
  843. "debug"
  844. else
  845. raise @vmType
  846. end
  847. end
  848. def emitRunCode(fileToRun)
  849. myLibPath = @libPath
  850. myLibPath = "" unless myLibPath
  851. $script.puts "export DYLD_LIBRARY_PATH=#{myLibPath.to_s.inspect}"
  852. $script.puts "export DYLD_FRAMEWORK_PATH=#{myLibPath.to_s.inspect}"
  853. $script.puts "#{path} #{fileToRun}"
  854. end
  855. end
  856. class StatsAccumulator
  857. def initialize
  858. @stats = []
  859. ($outer*$inner).times {
  860. @stats << Stats.new
  861. }
  862. end
  863. def statsForIteration(outerIteration, innerIteration)
  864. @stats[outerIteration*$inner + innerIteration]
  865. end
  866. def stats
  867. result = Stats.new
  868. @stats.each {
  869. | stat |
  870. result.add(yield stat)
  871. }
  872. result
  873. end
  874. def geometricMeanStats
  875. stats {
  876. | stat |
  877. stat.geometricMean
  878. }
  879. end
  880. def arithmeticMeanStats
  881. stats {
  882. | stat |
  883. stat.arithmeticMean
  884. }
  885. end
  886. end
  887. module Benchmark
  888. attr_accessor :benchmarkSuite
  889. attr_reader :name
  890. def fullname
  891. benchmarkSuite.name + "/" + name
  892. end
  893. def to_s
  894. fullname
  895. end
  896. end
  897. class SunSpiderBenchmark
  898. include Benchmark
  899. def initialize(name)
  900. @name = name
  901. end
  902. def emitRunCode(plan)
  903. emitBenchRunCode(fullname, plan, nil, ensureFile("SunSpider-#{@name}", "#{SUNSPIDER_PATH}/#{@name}.js"))
  904. end
  905. end
  906. class V8Benchmark
  907. include Benchmark
  908. def initialize(name)
  909. @name = name
  910. end
  911. def emitRunCode(plan)
  912. emitBenchRunCode(fullname, plan, nil, ensureFile("V8-#{@name}", "#{V8_PATH}/v8-#{@name}.js"))
  913. end
  914. end
  915. class KrakenBenchmark
  916. include Benchmark
  917. def initialize(name)
  918. @name = name
  919. end
  920. def emitRunCode(plan)
  921. emitBenchRunCode(fullname, plan, ensureFile("KrakenData-#{@name}", "#{KRAKEN_PATH}/#{@name}-data.js"), ensureFile("Kraken-#{@name}", "#{KRAKEN_PATH}/#{@name}.js"))
  922. end
  923. end
  924. class BenchmarkSuite
  925. def initialize(name, path, preferredMean)
  926. @name = name
  927. @path = path
  928. @preferredMean = preferredMean
  929. @benchmarks = []
  930. end
  931. def name
  932. @name
  933. end
  934. def to_s
  935. @name
  936. end
  937. def path
  938. @path
  939. end
  940. def add(benchmark)
  941. if not $benchmarkPattern or "#{@name}/#{benchmark.name}" =~ $benchmarkPattern
  942. benchmark.benchmarkSuite = self
  943. @benchmarks << benchmark
  944. end
  945. end
  946. def benchmarks
  947. @benchmarks
  948. end
  949. def benchmarkForName(name)
  950. result = @benchmarks.select{|v| v.name == name}
  951. raise unless result.length == 1
  952. result[0]
  953. end
  954. def empty?
  955. @benchmarks.empty?
  956. end
  957. def retain_if
  958. @benchmarks.delete_if {
  959. | benchmark |
  960. not yield benchmark
  961. }
  962. end
  963. def preferredMean
  964. @preferredMean
  965. end
  966. def computeMean(stat)
  967. stat.send @preferredMean
  968. end
  969. end
  970. class BenchRunPlan
  971. def initialize(benchmark, vm, iteration)
  972. @benchmark = benchmark
  973. @vm = vm
  974. @iteration = iteration
  975. end
  976. def benchmark
  977. @benchmark
  978. end
  979. def suite
  980. @benchmark.benchmarkSuite
  981. end
  982. def vm
  983. @vm
  984. end
  985. def iteration
  986. @iteration
  987. end
  988. def emitRunCode
  989. @benchmark.emitRunCode(self)
  990. end
  991. end
  992. class BenchmarkOnVM
  993. def initialize(benchmark, suiteOnVM)
  994. @benchmark = benchmark
  995. @suiteOnVM = suiteOnVM
  996. @stats = Stats.new
  997. end
  998. def to_s
  999. "#{@benchmark} on #{@suiteOnVM.vm}"
  1000. end
  1001. def benchmark
  1002. @benchmark
  1003. end
  1004. def vm
  1005. @suiteOnVM.vm
  1006. end
  1007. def vmStats
  1008. @suiteOnVM.vmStats
  1009. end
  1010. def suite
  1011. @benchmark.benchmarkSuite
  1012. end
  1013. def suiteOnVM
  1014. @suiteOnVM
  1015. end
  1016. def stats
  1017. @stats
  1018. end
  1019. def parseResult(result)
  1020. raise "VM mismatch; I've got #{vm} and they've got #{result.vm}" unless result.vm == vm
  1021. raise unless result.benchmark == @benchmark
  1022. @stats.add(result.time)
  1023. end
  1024. end
  1025. class SuiteOnVM < StatsAccumulator
  1026. def initialize(vm, vmStats, suite)
  1027. super()
  1028. @vm = vm
  1029. @vmStats = vmStats
  1030. @suite = suite
  1031. raise unless @vm.is_a? VM
  1032. raise unless @vmStats.is_a? StatsAccumulator
  1033. raise unless @suite.is_a? BenchmarkSuite
  1034. end
  1035. def to_s
  1036. "#{@suite} on #{@vm}"
  1037. end
  1038. def suite
  1039. @suite
  1040. end
  1041. def vm
  1042. @vm
  1043. end
  1044. def vmStats
  1045. raise unless @vmStats
  1046. @vmStats
  1047. end
  1048. end
  1049. class BenchPlan
  1050. def initialize(benchmarkOnVM, iteration)
  1051. @benchmarkOnVM = benchmarkOnVM
  1052. @iteration = iteration
  1053. end
  1054. def to_s
  1055. "#{@benchmarkOnVM} \##{@iteration+1}"
  1056. end
  1057. def benchmarkOnVM
  1058. @benchmarkOnVM
  1059. end
  1060. def benchmark
  1061. @benchmarkOnVM.benchmark
  1062. end
  1063. def suite
  1064. @benchmarkOnVM.suite
  1065. end
  1066. def vm
  1067. @benchmarkOnVM.vm
  1068. end
  1069. def iteration
  1070. @iteration
  1071. end
  1072. def parseResult(result)
  1073. raise unless result.plan == self
  1074. @benchmarkOnVM.parseResult(result)
  1075. @benchmarkOnVM.vmStats.statsForIteration(@iteration, result.innerIndex).add(result.time)
  1076. @benchmarkOnVM.suiteOnVM.statsForIteration(@iteration, result.innerIndex).add(result.time)
  1077. end
  1078. end
  1079. def lpad(str,chars)
  1080. if str.length>chars
  1081. str
  1082. else
  1083. "%#{chars}s"%(str)
  1084. end
  1085. end
  1086. def rpad(str,chars)
  1087. while str.length<chars
  1088. str+=" "
  1089. end
  1090. str
  1091. end
  1092. def center(str,chars)
  1093. while str.length<chars
  1094. str+=" "
  1095. if str.length<chars
  1096. str=" "+str
  1097. end
  1098. end
  1099. str
  1100. end
  1101. def statsToStr(stats)
  1102. if $inner*$outer == 1
  1103. string = numToStr(stats.mean)
  1104. raise unless string =~ /\./
  1105. left = $~.pre_match
  1106. right = $~.post_match
  1107. lpad(left,12)+"."+rpad(right,9)
  1108. else
  1109. lpad(numToStr(stats.mean),11)+"+-"+rpad(numToStr(stats.confInt),9)
  1110. end
  1111. end
  1112. def plural(num)
  1113. if num == 1
  1114. ""
  1115. else
  1116. "s"
  1117. end
  1118. end
  1119. def wrap(str, columns)
  1120. array = str.split
  1121. result = ""
  1122. curLine = array.shift
  1123. array.each {
  1124. | curStr |
  1125. if (curLine + " " + curStr).size > columns
  1126. result += curLine + "\n"
  1127. curLine = curStr
  1128. else
  1129. curLine += " " + curStr
  1130. end
  1131. }
  1132. result + curLine + "\n"
  1133. end
  1134. def runAndGetResults
  1135. results = nil
  1136. Dir.chdir(BENCH_DATA_PATH) {
  1137. IO.popen("sh ./runscript", "r") {
  1138. | inp |
  1139. results = inp.read
  1140. }
  1141. raise "Script did not complete correctly: #{$?}" unless $?.success?
  1142. }
  1143. raise unless results
  1144. results
  1145. end
  1146. def parseAndDisplayResults(results)
  1147. vmStatses = []
  1148. $vms.each {
  1149. vmStatses << StatsAccumulator.new
  1150. }
  1151. suitesOnVMs = []
  1152. suitesOnVMsForSuite = {}
  1153. $suites.each {
  1154. | suite |
  1155. suitesOnVMsForSuite[suite] = []
  1156. }
  1157. suitesOnVMsForVM = {}
  1158. $vms.each {
  1159. | vm |
  1160. suitesOnVMsForVM[vm] = []
  1161. }
  1162. benchmarksOnVMs = []
  1163. benchmarksOnVMsForBenchmark = {}
  1164. $benchmarks.each {
  1165. | benchmark |
  1166. benchmarksOnVMsForBenchmark[benchmark] = []
  1167. }
  1168. $vms.each_with_index {
  1169. | vm, vmIndex |
  1170. vmStats = vmStatses[vmIndex]
  1171. $suites.each {
  1172. | suite |
  1173. suiteOnVM = SuiteOnVM.new(vm, vmStats, suite)
  1174. suitesOnVMs << suiteOnVM
  1175. suitesOnVMsForSuite[suite] << suiteOnVM
  1176. suitesOnVMsForVM[vm] << suiteOnVM
  1177. suite.benchmarks.each {
  1178. | benchmark |
  1179. benchmarkOnVM = BenchmarkOnVM.new(benchmark, suiteOnVM)
  1180. benchmarksOnVMs << benchmarkOnVM
  1181. benchmarksOnVMsForBenchmark[benchmark] << benchmarkOnVM
  1182. }
  1183. }
  1184. }
  1185. plans = []
  1186. benchmarksOnVMs.each {
  1187. | benchmarkOnVM |
  1188. $outer.times {
  1189. | iteration |
  1190. plans << BenchPlan.new(benchmarkOnVM, iteration)
  1191. }
  1192. }
  1193. hostname = nil
  1194. hwmodel = nil
  1195. results.each_line {
  1196. | line |
  1197. line.chomp!
  1198. if line =~ /HOSTNAME:([^.]+)/
  1199. hostname = $1
  1200. elsif line =~ /HARDWARE:hw\.model: /
  1201. hwmodel = $~.post_match.chomp
  1202. else
  1203. result = ParsedResult.parse(plans, line.chomp)
  1204. if result
  1205. result.plan.parseResult(result)
  1206. end
  1207. end
  1208. }
  1209. # Compute the geomean of the preferred means of results on a SuiteOnVM
  1210. overallResults = []
  1211. $vms.each {
  1212. | vm |
  1213. result = Stats.new
  1214. $outer.times {
  1215. | outerIndex |
  1216. $inner.times {
  1217. | innerIndex |
  1218. curResult = Stats.new
  1219. suitesOnVMsForVM[vm].each {
  1220. | suiteOnVM |
  1221. # For a given iteration, suite, and VM, compute the suite's preferred mean
  1222. # over the data collected for all benchmarks in that suite. We'll have one
  1223. # sample per benchmark. For example on V8 this will be the geomean of 1
  1224. # sample for crypto, 1 sample for deltablue, and so on, and 1 sample for
  1225. # splay.
  1226. curResult.add(suiteOnVM.suite.computeMean(suiteOnVM.statsForIteration(outerIndex, innerIndex)))
  1227. }
  1228. # curResult now holds 1 sample for each of the means computed in the above
  1229. # loop. Compute the geomean over this, and store it.
  1230. result.add(curResult.geometricMean)
  1231. }
  1232. }
  1233. # $overallResults will have a Stats for each VM. That Stats object will hold
  1234. # $inner*$outer geomeans, allowing us to compute the arithmetic mean and
  1235. # confidence interval of the geomeans of preferred means. Convoluted, but
  1236. # useful and probably sound.
  1237. overallResults << result
  1238. }
  1239. if $verbosity >= 2
  1240. benchmarksOnVMs.each {
  1241. | benchmarkOnVM |
  1242. $stderr.puts "#{benchmarkOnVM}: #{benchmarkOnVM.stats}"
  1243. }
  1244. $vms.each_with_index {
  1245. | vm, vmIndex |
  1246. vmStats = vmStatses[vmIndex]
  1247. $stderr.puts "#{vm} (arithmeticMean): #{vmStats.arithmeticMeanStats}"
  1248. $stderr.puts "#{vm} (geometricMean): #{vmStats.geometricMeanStats}"
  1249. }
  1250. end
  1251. reportName =
  1252. (if ($vms.collect {
  1253. | vm |
  1254. vm.nameKind
  1255. }.index :auto)
  1256. ""
  1257. else
  1258. $vms.collect {
  1259. | vm |
  1260. vm.to_s
  1261. }.join("_") + "_"
  1262. end) +
  1263. ($suites.collect {
  1264. | suite |
  1265. suite.to_s
  1266. }.join("")) + "_" +
  1267. (if hostname
  1268. hostname + "_"
  1269. else
  1270. ""
  1271. end)+
  1272. (begin
  1273. time = Time.now
  1274. "%04d%02d%02d_%02d%02d" %
  1275. [ time.year, time.month, time.day,
  1276. time.hour, time.min ]
  1277. end) +
  1278. "_benchReport.txt"
  1279. unless $brief
  1280. puts "Generating benchmark report at #{reportName}"
  1281. end
  1282. outp = $stdout
  1283. begin
  1284. outp = File.open(reportName,"w")
  1285. rescue => e
  1286. $stderr.puts "Error: could not save report to #{reportName}: #{e}"
  1287. $stderr.puts
  1288. end
  1289. def createVMsString
  1290. result = ""
  1291. result += " " if $suites.size > 1
  1292. result += rpad("", $benchpad)
  1293. result += " "
  1294. $vms.size.times {
  1295. | index |
  1296. if index != 0
  1297. result += " "+NoChange.new(0).shortForm
  1298. end
  1299. result += lpad(center($vms[index].name, 9+9+2), 11+9+2)
  1300. }
  1301. result += " "
  1302. if $vms.size >= 3
  1303. result += center("#{$vms[-1].name} v. #{$vms[0].name}",26)
  1304. elsif $vms.size >= 2
  1305. result += " "*26
  1306. end
  1307. result
  1308. end
  1309. columns = [createVMsString.size, 78].max
  1310. outp.print "Benchmark report for "
  1311. if $suites.size == 1
  1312. outp.print $suites[0].to_s
  1313. elsif $suites.size == 2
  1314. outp.print "#{$suites[0]} and #{$suites[1]}"
  1315. else
  1316. outp.print "#{$suites[0..-2].join(', ')}, and #{$suites[-1]}"
  1317. end
  1318. if hostname
  1319. outp.print " on #{hostname}"
  1320. end
  1321. if hwmodel
  1322. outp.print " (#{hwmodel})"
  1323. end
  1324. outp.puts "."
  1325. outp.puts
  1326. # This looks stupid; revisit later.
  1327. if false
  1328. $suites.each {
  1329. | suite |
  1330. outp.puts "#{suite} at #{suite.path}"
  1331. }
  1332. outp.puts
  1333. end
  1334. outp.puts "VMs tested:"
  1335. $vms.each {
  1336. | vm |
  1337. outp.print "\"#{vm.name}\" at #{vm.origPath}"
  1338. if vm.svnRevision
  1339. outp.print " (r#{vm.svnRevision})"
  1340. end
  1341. outp.puts
  1342. }
  1343. outp.puts
  1344. outp.puts wrap("Collected #{$outer*$inner} sample#{plural($outer*$inner)} per benchmark/VM, "+
  1345. "with #{$outer} VM invocation#{plural($outer)} per benchmark."+
  1346. (if $rerun > 1 then (" Ran #{$rerun} benchmark iterations, and measured the "+
  1347. "total time of those iterations, for each sample.")
  1348. else "" end)+
  1349. (if $measureGC == true then (" No manual garbage collection invocations were "+
  1350. "emitted.")
  1351. elsif $measureGC then (" Emitted a call to gc() between sample measurements for "+
  1352. "all VMs except #{$measureGC}.")
  1353. else (" Emitted a call to gc() between sample measurements.") end)+
  1354. (if $warmup == 0 then (" Did not include any warm-up iterations; measurements "+
  1355. "began with the very first iteration.")
  1356. else (" Used #{$warmup*$rerun} benchmark iteration#{plural($warmup*$rerun)} per VM "+
  1357. "invocation for warm-up.") end)+
  1358. (case $timeMode
  1359. when :preciseTime then (" Used the jsc-specific preciseTime() function to get "+
  1360. "microsecond-level timing.")
  1361. when :date then (" Used the portable Date.now() method to get millisecond-"+
  1362. "level timing.")
  1363. else raise end)+
  1364. " Reporting benchmark execution times with 95% confidence "+
  1365. "intervals in milliseconds.",
  1366. columns)
  1367. outp.puts
  1368. def printVMs(outp)
  1369. outp.puts createVMsString
  1370. end
  1371. def summaryStats(outp, accumulators, name, &proc)
  1372. outp.print " " if $suites.size > 1
  1373. outp.print rpad(name, $benchpad)
  1374. outp.print " "
  1375. accumulators.size.times {
  1376. | index |
  1377. if index != 0
  1378. outp.print " "+accumulators[index].stats(&proc).compareTo(accumulators[index-1].stats(&proc)).shortForm
  1379. end
  1380. outp.print statsToStr(accumulators[index].stats(&proc))
  1381. }
  1382. if accumulators.size>=2
  1383. outp.print(" "+accumulators[-1].stats(&proc).compareTo(accumulators[0].stats(&proc)).longForm)
  1384. end
  1385. outp.puts
  1386. end
  1387. def meanName(currentMean, preferredMean)
  1388. result = "<#{currentMean}>"
  1389. if "#{currentMean}Mean" == preferredMean.to_s
  1390. result += " *"
  1391. end
  1392. result
  1393. end
  1394. def allSummaryStats(outp, accumulators, preferredMean)
  1395. summaryStats(outp, accumulators, meanName("arithmetic", preferredMean)) {
  1396. | stat |
  1397. stat.arithmeticMean
  1398. }
  1399. summaryStats(outp, accumulators, meanName("geometric", preferredMean)) {
  1400. | stat |
  1401. stat.geometricMean
  1402. }
  1403. summaryStats(outp, accumulators, meanName("harmonic", preferredMean)) {
  1404. | stat |
  1405. stat.harmonicMean
  1406. }
  1407. end
  1408. $suites.each {
  1409. | suite |
  1410. printVMs(outp)
  1411. if $suites.size > 1
  1412. outp.puts "#{suite.name}:"
  1413. else
  1414. outp.puts
  1415. end
  1416. suite.benchmarks.each {
  1417. | benchmark |
  1418. outp.print " " if $suites.size > 1
  1419. outp.print rpad(benchmark.name, $benchpad)
  1420. outp.print " "
  1421. myConfigs = benchmarksOnVMsForBenchmark[benchmark]
  1422. myConfigs.size.times {
  1423. | index |
  1424. if index != 0
  1425. outp.print " "+myConfigs[index].stats.compareTo(myConfigs[index-1].stats).shortForm
  1426. end
  1427. outp.print statsToStr(myConfigs[index].stats)
  1428. }
  1429. if $vms.size>=2
  1430. outp.print(" "+myConfigs[-1].stats.compareTo(myConfigs[0].stats).to_s)
  1431. end
  1432. outp.puts
  1433. }
  1434. outp.puts
  1435. allSummaryStats(outp, suitesOnVMsForSuite[suite], suite.preferredMean)
  1436. outp.puts if $suites.size > 1
  1437. }
  1438. if $suites.size > 1
  1439. printVMs(outp)
  1440. outp.puts "All benchmarks:"
  1441. allSummaryStats(outp, vmStatses, nil)
  1442. outp.puts
  1443. printVMs(outp)
  1444. outp.puts "Geomean of preferred means:"
  1445. outp.print " "
  1446. outp.print rpad("<scaled-result>", $benchpad)
  1447. outp.print " "
  1448. $vms.size.times {
  1449. | index |
  1450. if index != 0
  1451. outp.print " "+overallResults[index].compareTo(overallResults[index-1]).shortForm
  1452. end
  1453. outp.print statsToStr(overallResults[index])
  1454. }
  1455. if overallResults.size>=2
  1456. outp.print(" "+overallResults[-1].compareTo(overallResults[0]).longForm)
  1457. end
  1458. outp.puts
  1459. end
  1460. outp.puts
  1461. if outp != $stdout
  1462. outp.close
  1463. end
  1464. if outp != $stdout and not $brief
  1465. puts
  1466. File.open(reportName) {
  1467. | inp |
  1468. puts inp.read
  1469. }
  1470. end
  1471. if $brief
  1472. puts(overallResults.collect{|stats| stats.mean}.join("\t"))
  1473. puts(overallResults.collect{|stats| stats.confInt}.join("\t"))
  1474. end
  1475. end
  1476. begin
  1477. $sawBenchOptions = false
  1478. def resetBenchOptionsIfNecessary
  1479. unless $sawBenchOptions
  1480. $includeSunSpider = false
  1481. $includeV8 = false
  1482. $includeKraken = false
  1483. $sawBenchOptions = true
  1484. end
  1485. end
  1486. GetoptLong.new(['--rerun', GetoptLong::REQUIRED_ARGUMENT],
  1487. ['--inner', GetoptLong::REQUIRED_ARGUMENT],
  1488. ['--outer', GetoptLong::REQUIRED_ARGUMENT],
  1489. ['--warmup', GetoptLong::REQUIRED_ARGUMENT],
  1490. ['--timing-mode', GetoptLong::REQUIRED_ARGUMENT],
  1491. ['--sunspider-only', GetoptLong::NO_ARGUMENT],
  1492. ['--v8-only', GetoptLong::NO_ARGUMENT],
  1493. ['--kraken-only', GetoptLong::NO_ARGUMENT],
  1494. ['--exclude-sunspider', GetoptLong::NO_ARGUMENT],
  1495. ['--exclude-v8', GetoptLong::NO_ARGUMENT],
  1496. ['--exclude-kraken', GetoptLong::NO_ARGUMENT],
  1497. ['--sunspider', GetoptLong::NO_ARGUMENT],
  1498. ['--v8', GetoptLong::NO_ARGUMENT],
  1499. ['--kraken', GetoptLong::NO_ARGUMENT],
  1500. ['--benchmarks', GetoptLong::REQUIRED_ARGUMENT],
  1501. ['--measure-gc', GetoptLong::OPTIONAL_ARGUMENT],
  1502. ['--force-vm-kind', GetoptLong::REQUIRED_ARGUMENT],
  1503. ['--force-vm-copy', GetoptLong::NO_ARGUMENT],
  1504. ['--dont-copy-vms', GetoptLong::NO_ARGUMENT],
  1505. ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
  1506. ['--brief', GetoptLong::NO_ARGUMENT],
  1507. ['--silent', GetoptLong::NO_ARGUMENT],
  1508. ['--remote', GetoptLong::REQUIRED_ARGUMENT],
  1509. ['--local', GetoptLong::NO_ARGUMENT],
  1510. ['--ssh-options', GetoptLong::REQUIRED_ARGUMENT],
  1511. ['--slave', GetoptLong::NO_ARGUMENT],
  1512. ['--prepare-only', GetoptLong::NO_ARGUMENT],
  1513. ['--analyze', GetoptLong::REQUIRED_ARGUMENT],
  1514. ['--vms', GetoptLong::REQUIRED_ARGUMENT],
  1515. ['--help', '-h', GetoptLong::NO_ARGUMENT]).each {
  1516. | opt, arg |
  1517. case opt
  1518. when '--rerun'
  1519. $rerun = intArg(opt,arg,1,nil)
  1520. when '--inner'
  1521. $inner = intArg(opt,arg,1,nil)
  1522. when '--outer'
  1523. $outer = intArg(opt,arg,1,nil)
  1524. when '--warmup'
  1525. $warmup = intArg(opt,arg,0,nil)
  1526. when '--timing-mode'
  1527. if arg.upcase == "PRECISETIME"
  1528. $timeMode = :preciseTime
  1529. elsif arg.upcase == "DATE"
  1530. $timeMode = :date
  1531. elsif arg.upcase == "AUTO"
  1532. $timeMode = :auto
  1533. else
  1534. quickFail("Expected either 'preciseTime', 'date', or 'auto' for --time-mode, but got '#{arg}'.",
  1535. "Invalid argument for command-line option")
  1536. end
  1537. when '--force-vm-kind'
  1538. if arg.upcase == "JSC"
  1539. $forceVMKind = :jsc
  1540. elsif arg.upcase == "DUMPRENDERTREE"
  1541. $forceVMKind = :dumpRenderTree
  1542. elsif arg.upcase == "AUTO"
  1543. $forceVMKind = nil
  1544. else
  1545. quickFail("Expected either 'jsc' or 'DumpRenderTree' for --force-vm-kind, but got '#{arg}'.",
  1546. "Invalid argument for command-line option")
  1547. end
  1548. when '--force-vm-copy'
  1549. $needToCopyVMs = true
  1550. when '--dont-copy-vms'
  1551. $dontCopyVMs = true
  1552. when '--sunspider-only'
  1553. $includeV8 = false
  1554. $includeKraken = false
  1555. when '--v8-only'
  1556. $includeSunSpider = false
  1557. $includeKraken = false
  1558. when '--kraken-only'
  1559. $includeSunSpider = false
  1560. $includeV8 = false
  1561. when '--exclude-sunspider'
  1562. $includeSunSpider = false
  1563. when '--exclude-v8'
  1564. $includeV8 = false
  1565. when '--exclude-kraken'
  1566. $includeKraken = false
  1567. when '--sunspider'
  1568. resetBenchOptionsIfNecessary
  1569. $includeSunSpider = true
  1570. when '--v8'
  1571. resetBenchOptionsIfNecessary
  1572. $includeV8 = true
  1573. when '--kraken'
  1574. resetBenchOptionsIfNecessary
  1575. $includeKraken = true
  1576. when '--benchmarks'
  1577. $benchmarkPattern = Regexp.new(arg)
  1578. when '--measure-gc'
  1579. if arg == ''
  1580. $measureGC = true
  1581. else
  1582. $measureGC = arg
  1583. end
  1584. when '--verbose'
  1585. $verbosity += 1
  1586. when '--brief'
  1587. $brief = true
  1588. when '--silent'
  1589. $silent = true
  1590. when '--remote'
  1591. $remoteHosts += arg.split(',')
  1592. $needToCopyVMs = true
  1593. when '--ssh-options'
  1594. $sshOptions << arg
  1595. when '--local'
  1596. $alsoLocal = true
  1597. when '--prepare-only'
  1598. $run = false
  1599. when '--analyze'
  1600. $prepare = false
  1601. $run = false
  1602. $analyze << arg
  1603. when '--help'
  1604. usage
  1605. else
  1606. raise "bad option: #{opt}"
  1607. end
  1608. }
  1609. # If the --dont-copy-vms option was passed, it overrides the --force-vm-copy option.
  1610. if $dontCopyVMs
  1611. $needToCopyVMs = false
  1612. end
  1613. SUNSPIDER = BenchmarkSuite.new("SunSpider", SUNSPIDER_PATH, :arithmeticMean)
  1614. ["3d-cube", "3d-morph", "3d-raytrace", "access-binary-trees",
  1615. "access-fannkuch", "access-nbody", "access-nsieve",
  1616. "bitops-3bit-bits-in-byte", "bitops-bits-in-byte", "bitops-bitwise-and",
  1617. "bitops-nsieve-bits", "controlflow-recursive", "crypto-aes",
  1618. "crypto-md5", "crypto-sha1", "date-format-tofte", "date-format-xparb",
  1619. "math-cordic", "math-partial-sums", "math-spectral-norm", "regexp-dna",
  1620. "string-base64", "string-fasta", "string-tagcloud",
  1621. "string-unpack-code", "string-validate-input"].each {
  1622. | name |
  1623. SUNSPIDER.add SunSpiderBenchmark.new(name)
  1624. }
  1625. V8 = BenchmarkSuite.new("V8", V8_PATH, :geometricMean)
  1626. ["crypto", "deltablue", "earley-boyer", "raytrace",
  1627. "regexp", "richards", "splay"].each {
  1628. | name |
  1629. V8.add V8Benchmark.new(name)
  1630. }
  1631. KRAKEN = BenchmarkSuite.new("Kraken", KRAKEN_PATH, :arithmeticMean)
  1632. ["ai-astar", "audio-beat-detection", "audio-dft", "audio-fft",
  1633. "audio-oscillator", "imaging-darkroom", "imaging-desaturate",
  1634. "imaging-gaussian-blur", "json-parse-financial",
  1635. "json-stringify-tinderbox", "stanford-crypto-aes",
  1636. "stanford-crypto-ccm", "stanford-crypto-pbkdf2",
  1637. "stanford-crypto-sha256-iterative"].each {
  1638. | name |
  1639. KRAKEN.add KrakenBenchmark.new(name)
  1640. }
  1641. ARGV.each {
  1642. | vm |
  1643. if vm =~ /([a-zA-Z0-9_ ]+):/
  1644. name = $1
  1645. nameKind = :given
  1646. vm = $~.post_match
  1647. else
  1648. name = "Conf\##{$vms.length+1}"
  1649. nameKind = :auto
  1650. end
  1651. $stderr.puts "#{name}: #{vm}" if $verbosity >= 1
  1652. $vms << VM.new(Pathname.new(vm).realpath, name, nameKind, nil)
  1653. }
  1654. if $vms.empty?
  1655. quickFail("Please specify at least on configuraiton on the command line.",
  1656. "Insufficient arguments")
  1657. end
  1658. $vms.each {
  1659. | vm |
  1660. if vm.vmType != :jsc and $timeMode != :date
  1661. $timeMode = :date
  1662. $stderr.puts "Warning: using Date.now() instead of preciseTime() because #{vm} doesn't support the latter."
  1663. end
  1664. }
  1665. if FileTest.exist? BENCH_DATA_PATH
  1666. cmd = "rm -rf #{BENCH_DATA_PATH}"
  1667. $stderr.puts ">> #{cmd}" if $verbosity >= 2
  1668. raise unless system cmd
  1669. end
  1670. Dir.mkdir BENCH_DATA_PATH
  1671. if $needToCopyVMs
  1672. canCopyIntoBenchPath = true
  1673. $vms.each {
  1674. | vm |
  1675. canCopyIntoBenchPath = false unless vm.canCopyIntoBenchPath
  1676. }
  1677. if canCopyIntoBenchPath
  1678. $vms.each {
  1679. | vm |
  1680. $stderr.puts "Copying #{vm} into #{BENCH_DATA_PATH}..."
  1681. vm.copyIntoBenchPath
  1682. }
  1683. $stderr.puts "All VMs are in place."
  1684. else
  1685. $stderr.puts "Warning: don't know how to copy some VMs into #{BENCH_DATA_PATH}, so I won't do it."
  1686. end
  1687. end
  1688. if $measureGC and $measureGC != true
  1689. found = false
  1690. $vms.each {
  1691. | vm |
  1692. if vm.name == $measureGC
  1693. found = true
  1694. end
  1695. }
  1696. unless found
  1697. $stderr.puts "Warning: --measure-gc option ignored because no VM is named #{$measureGC}"
  1698. end
  1699. end
  1700. if $outer*$inner == 1
  1701. $stderr.puts "Warning: will only collect one sample per benchmark/VM. Confidence interval calculation will fail."
  1702. end
  1703. $stderr.puts "Using timeMode = #{$timeMode}." if $verbosity >= 1
  1704. $suites = []
  1705. if $includeSunSpider and not SUNSPIDER.empty?
  1706. $suites << SUNSPIDER
  1707. end
  1708. if $includeV8 and not V8.empty?
  1709. $suites << V8
  1710. end
  1711. if $includeKraken and not KRAKEN.empty?
  1712. $suites << KRAKEN
  1713. end
  1714. $benchmarks = []
  1715. $suites.each {
  1716. | suite |
  1717. $benchmarks += suite.benchmarks
  1718. }
  1719. $runPlans = []
  1720. $vms.each {
  1721. | vm |
  1722. $benchmarks.each {
  1723. | benchmark |
  1724. $outer.times {
  1725. | iteration |
  1726. $runPlans << BenchRunPlan.new(benchmark, vm, iteration)
  1727. }
  1728. }
  1729. }
  1730. $runPlans.shuffle!
  1731. $suitepad = $suites.collect {
  1732. | suite |
  1733. suite.to_s.size
  1734. }.max + 1
  1735. $benchpad = ($benchmarks +
  1736. ["<arithmetic> *", "<geometric> *", "<harmonic> *"]).collect {
  1737. | benchmark |
  1738. if benchmark.respond_to? :name
  1739. benchmark.name.size
  1740. else
  1741. benchmark.size
  1742. end
  1743. }.max + 1
  1744. $vmpad = $vms.collect {
  1745. | vm |
  1746. vm.to_s.size
  1747. }.max + 1
  1748. if $prepare
  1749. File.open("#{BENCH_DATA_PATH}/runscript", "w") {
  1750. | file |
  1751. file.puts "echo \"HOSTNAME:\\c\""
  1752. file.puts "hostname"
  1753. file.puts "echo"
  1754. file.puts "echo \"HARDWARE:\\c\""
  1755. file.puts "/usr/sbin/sysctl hw.model"
  1756. file.puts "echo"
  1757. file.puts "set -e"
  1758. $script = file
  1759. $runPlans.each_with_index {
  1760. | plan, idx |
  1761. if $verbosity == 0 and not $silent
  1762. text1 = lpad(idx.to_s,$runPlans.size.to_s.size)+"/"+$runPlans.size.to_s
  1763. text2 = plan.benchmark.to_s+"/"+plan.vm.to_s
  1764. file.puts("echo "+("\r#{text1} #{rpad(text2,$suitepad+1+$benchpad+1+$vmpad)}".inspect)[0..-2]+"\\c\" 1>&2")
  1765. file.puts("echo "+("\r#{text1} #{text2}".inspect)[0..-2]+"\\c\" 1>&2")
  1766. end
  1767. plan.emitRunCode
  1768. }
  1769. if $verbosity == 0 and not $silent
  1770. file.puts("echo "+("\r#{$runPlans.size}/#{$runPlans.size} #{' '*($suitepad+1+$benchpad+1+$vmpad)}".inspect)[0..-2]+"\\c\" 1>&2")
  1771. file.puts("echo "+("\r#{$runPlans.size}/#{$runPlans.size}".inspect)+" 1>&2")
  1772. end
  1773. }
  1774. end
  1775. if $run
  1776. unless $remoteHosts.empty?
  1777. $stderr.puts "Packaging benchmarking directory for remote hosts..." if $verbosity==0
  1778. Dir.chdir(TEMP_PATH) {
  1779. cmd = "tar -czf payload.tar.gz benchdata"
  1780. $stderr.puts ">> #{cmd}" if $verbosity>=2
  1781. raise unless system(cmd)
  1782. }
  1783. def grokHost(host)
  1784. if host =~ /:([0-9]+)$/
  1785. "-p " + $1 + " " + $~.pre_match.inspect
  1786. else
  1787. host.inspect
  1788. end
  1789. end
  1790. def sshRead(host, command)
  1791. cmd = "ssh #{$sshOptions.collect{|x| x.inspect}.join(' ')} #{grokHost(host)} #{command.inspect}"
  1792. $stderr.puts ">> #{cmd}" if $verbosity>=2
  1793. result = ""
  1794. IO.popen(cmd, "r") {
  1795. | inp |
  1796. inp.each_line {
  1797. | line |
  1798. $stderr.puts "#{host}: #{line}" if $verbosity>=2
  1799. result += line
  1800. }
  1801. }
  1802. raise "#{$?}" unless $?.success?
  1803. result
  1804. end
  1805. def sshWrite(host, command, data)
  1806. cmd = "ssh #{$sshOptions.collect{|x| x.inspect}.join(' ')} #{grokHost(host)} #{command.inspect}"
  1807. $stderr.puts ">> #{cmd}" if $verbosity>=2
  1808. IO.popen(cmd, "w") {
  1809. | outp |
  1810. outp.write(data)
  1811. }
  1812. raise "#{$?}" unless $?.success?
  1813. end
  1814. $remoteHosts.each {
  1815. | host |
  1816. $stderr.puts "Sending benchmark payload to #{host}..." if $verbosity==0
  1817. remoteTempPath = JSON::parse(sshRead(host, "cat ~/.bencher"))["tempPath"]
  1818. raise unless remoteTempPath
  1819. sshWrite(host, "cd #{remoteTempPath.inspect} && rm -rf benchdata && tar -xz", IO::read("#{TEMP_PATH}/payload.tar.gz"))
  1820. $stderr.puts "Running on #{host}..." if $verbosity==0
  1821. parseAndDisplayResults(sshRead(host, "cd #{(remoteTempPath+'/benchdata').inspect} && sh runscript"))
  1822. }
  1823. end
  1824. if not $remoteHosts.empty? and $alsoLocal
  1825. $stderr.puts "Running locally..."
  1826. end
  1827. if $remoteHosts.empty? or $alsoLocal
  1828. parseAndDisplayResults(runAndGetResults)
  1829. end
  1830. end
  1831. $analyze.each_with_index {
  1832. | filename, index |
  1833. if index >= 1
  1834. puts
  1835. end
  1836. parseAndDisplayResults(IO::read(filename))
  1837. }
  1838. if $prepare and not $run and $analyze.empty?
  1839. puts wrap("Benchmarking script and data are in #{BENCH_DATA_PATH}. You can run "+
  1840. "the benchmarks and get the results by doing:", 78)
  1841. puts
  1842. puts "cd #{BENCH_DATA_PATH}"
  1843. puts "sh runscript > results.txt"
  1844. puts
  1845. puts wrap("Then you can analyze the results by running bencher with the same arguments "+
  1846. "as now, but replacing --prepare-only with --analyze results.txt.", 78)
  1847. end
  1848. rescue => e
  1849. fail(e)
  1850. end