PageRenderTime 48ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/benchmark/lib/benchmark/ips.rb

https://github.com/johnbender/rubinius
Ruby | 234 lines | 162 code | 65 blank | 7 comment | 18 complexity | 5a865565c6c0bbb3d352aacd66adebef MD5 | raw file
  1. # encoding: utf-8
  2. require 'benchmark/timing'
  3. require 'benchmark/compare'
  4. module Benchmark
  5. class IPSReport
  6. def initialize(label, us, iters, ips, ips_sd, cycles)
  7. @label = label
  8. @microseconds = us
  9. @iterations = iters
  10. @ips = ips
  11. @ips_sd = ips_sd
  12. @measurement_cycle = cycles
  13. end
  14. attr_reader :label, :microseconds, :iterations, :ips, :ips_sd, :measurement_cycle
  15. def seconds
  16. @microseconds.to_f / 1_000_000.0
  17. end
  18. def stddev_percentage
  19. 100.0 * (@ips_sd.to_f / @ips.to_f)
  20. end
  21. alias_method :runtime, :seconds
  22. def body
  23. left = "%10.1f (±%.1f%%) i/s" % [ips, stddev_percentage]
  24. left.ljust(20) + (" - %10d in %10.6fs (cycle=%d)" %
  25. [@iterations, runtime, @measurement_cycle])
  26. end
  27. def header
  28. @label.rjust(20)
  29. end
  30. def to_s
  31. "#{header} #{body}"
  32. end
  33. def display
  34. STDOUT.puts to_s
  35. end
  36. end
  37. class IPSJob
  38. class Entry
  39. def initialize(label, action)
  40. @label = label
  41. if action.kind_of? String
  42. compile action
  43. @action = self
  44. @as_action = true
  45. else
  46. unless action.respond_to? :call
  47. raise ArgumentError, "invalid action, must respond to #call"
  48. end
  49. @action = action
  50. if action.respond_to? :arity and action.arity > 0
  51. @call_loop = true
  52. else
  53. @call_loop = false
  54. end
  55. @as_action = false
  56. end
  57. end
  58. attr_reader :label, :action
  59. def as_action?
  60. @as_action
  61. end
  62. def call_times(times)
  63. return @action.call(times) if @call_loop
  64. act = @action
  65. i = 0
  66. while i < times
  67. act.call
  68. i += 1
  69. end
  70. end
  71. def compile(str)
  72. m = (class << self; self; end)
  73. code = <<-CODE
  74. def call_times(__total);
  75. __i = 0
  76. while __i < __total
  77. #{str};
  78. __i += 1
  79. end
  80. end
  81. CODE
  82. m.class_eval code
  83. end
  84. end
  85. def initialize
  86. @list = []
  87. @compare = false
  88. end
  89. attr_reader :compare
  90. def compare!
  91. @compare = true
  92. end
  93. #
  94. # Registers the given label and block pair in the job list.
  95. #
  96. def item(label="", str=nil, &blk) # :yield:
  97. if blk and str
  98. raise ArgmentError, "specify a block and a str, but not both"
  99. end
  100. action = str || blk
  101. raise ArgmentError, "no block or string" unless action
  102. @list.push Entry.new(label, action)
  103. self
  104. end
  105. alias_method :report, :item
  106. # An array of 2-element arrays, consisting of label and block pairs.
  107. attr_reader :list
  108. end
  109. def ips(time=5, warmup=2)
  110. suite = nil
  111. sync, STDOUT.sync = STDOUT.sync, true
  112. if defined? Benchmark::Suite and Suite.current
  113. suite = Benchmark::Suite.current
  114. end
  115. job = IPSJob.new
  116. yield job
  117. reports = []
  118. job.list.each do |item|
  119. suite.warming item.label, warmup if suite
  120. Timing.clean_env
  121. if !suite or !suite.quiet?
  122. if item.label.size > 20
  123. STDOUT.print "#{item.label}\n#{' ' * 20}"
  124. else
  125. STDOUT.print item.label.rjust(20)
  126. end
  127. end
  128. before = Time.now
  129. target = Time.now + warmup
  130. warmup_iter = 0
  131. while Time.now < target
  132. item.call_times(1)
  133. warmup_iter += 1
  134. end
  135. after = Time.now
  136. warmup_time = (after.to_f - before.to_f) * 1_000_000.0
  137. # calculate the time to run approx 100ms
  138. cycles_per_100ms = ((100_000 / warmup_time) * warmup_iter).to_i
  139. cycles_per_100ms = 1 if cycles_per_100ms <= 0
  140. suite.warmup_stats warmup_time, cycles_per_100ms if suite
  141. Timing.clean_env
  142. suite.running item.label, time if suite
  143. iter = 0
  144. target = Time.now + time
  145. measurements = []
  146. while Time.now < target
  147. before = Time.now
  148. item.call_times cycles_per_100ms
  149. after = Time.now
  150. iter += cycles_per_100ms
  151. measurements << ((after.to_f - before.to_f) * 1_000_000.0)
  152. end
  153. measured_us = measurements.inject(0) { |a,i| a + i }
  154. seconds = measured_us.to_f / 1_000_000.0
  155. all_ips = measurements.map { |i| cycles_per_100ms.to_f / (i.to_f / 1_000_000) }
  156. avg_ips = Timing.mean(all_ips)
  157. sd_ips = Timing.stddev(all_ips).round
  158. rep = IPSReport.new(item.label, measured_us, iter, avg_ips, sd_ips, cycles_per_100ms)
  159. STDOUT.puts " #{rep.body}" if !suite or !suite.quiet?
  160. suite.add_report rep, caller(1).first if suite
  161. STDOUT.sync = sync
  162. reports << rep
  163. end
  164. if job.compare
  165. Benchmark.compare *reports
  166. end
  167. end
  168. module_function :ips
  169. end