File: ips.rb

package info (click to toggle)
ruby-benchmark-ips 2.14.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 224 kB
  • sloc: ruby: 1,257; makefile: 11
file content (206 lines) | stat: -rw-r--r-- 6,647 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# encoding: utf-8
require 'benchmark/timing'
require 'benchmark/compare'
require 'benchmark/ips/stats/stats_metric'
require 'benchmark/ips/stats/sd'
require 'benchmark/ips/stats/bootstrap'
require 'benchmark/ips/report'
require 'benchmark/ips/job/entry'
require 'benchmark/ips/job/stream_report'
require 'benchmark/ips/job/multi_report'
require 'benchmark/ips/job'

# Performance benchmarking library
module Benchmark
  # Benchmark in iterations per second, no more guessing!
  #
  # See Benchmark.ips for documentation on using this gem~
  #
  # @see {https://github.com/evanphx/benchmark-ips}
  module IPS

    # Benchmark-ips Gem version.
    VERSION = "2.14.0"

    # CODENAME of current version.
    CODENAME = "Akagi"

    # Measure code in block, each code's benchmarked result will display in
    # iteration per second with standard deviation in given time.
    # @param time [Integer] Specify how long should benchmark your code in seconds.
    # @param warmup [Integer] Specify how long should Warmup time run in seconds.
    # @return [Report]
    def ips(*args)
      if args[0].is_a?(Hash)
        time, warmup, quiet = args[0].values_at(:time, :warmup, :quiet)
      else
        time, warmup, quiet = args
      end

      sync, $stdout.sync = $stdout.sync, true

      job = Job.new

      job_opts = {}
      job_opts[:time] = time unless time.nil?
      job_opts[:warmup] = warmup unless warmup.nil?
      job_opts[:quiet] = quiet unless quiet.nil?

      job.config job_opts

      yield job

      job.load_held_results

      job.run

      if job.run_single? && job.all_results_have_been_run?
        job.clear_held_results
      else
        job.save_held_results
        puts '', 'Pausing here -- run Ruby again to measure the next benchmark...' if job.run_single?
      end

      $stdout.sync = sync
      job.run_comparison
      job.generate_json

      report = job.full_report

      if ENV['SHARE'] || ENV['SHARE_URL']
        require 'benchmark/ips/share'
        share = Share.new report, job
        share.share
      end

      report
    end

    # Quickly compare multiple methods on the same object.
    # @param methods [Symbol...] A list of method names (as symbols) to compare.
    # @param receiver [Object] The object on which to call the methods. Defaults to Kernel.
    # @param opts [Hash] Additional options for customizing the benchmark.
    # @option opts [Integer] :warmup The number of seconds to warm up the benchmark.
    # @option opts [Integer] :time The number of seconds to run the benchmark.
    #
    # @example Compare String#upcase and String#downcase
    #   ips_quick(:upcase, :downcase, on: "hello")
    #
    # @example Compare two methods you just defined, with a custom warmup.
    #   def add; 1+1; end
    #   def sub; 2-1; end
    #   ips_quick(:add, :sub, warmup: 10)
    def ips_quick(*methods, on: Kernel, **opts)
      ips(opts) do |x|
        x.compare!

        methods.each do |name|
          x.report(name) do |iter|
            iter.times { on.__send__ name }
          end
        end
      end
    end

    # Set options for running the benchmarks.
    # :format => [:human, :raw]
    #    :human format narrows precision and scales results for readability
    #    :raw format displays 6 places of precision and exact iteration counts
    def self.options
      @options ||= {:format => :human}
    end

    module Helpers
      SUFFIXES = ['', 'k', 'M', 'B', 'T', 'Q'].freeze

      def scale(value)
        scale = (Math.log10(value) / 3).to_i
        scale = 0 if scale < 0 || scale >= SUFFIXES.size
        suffix = SUFFIXES[scale]
        scaled_value = value.to_f / (1000 ** scale)

        "%10.3f#{suffix}" % scaled_value
      end
      module_function :scale

      def humanize_duration(duration_ns)
        if duration_ns < 1000
          "%.2f ns" % duration_ns
        elsif duration_ns < 1_000_000
          "%.2f μs" % (duration_ns / 1000)
        elsif duration_ns < 1_000_000_000
          "%.2f ms" % (duration_ns / 1_000_000)
        else
          "%.2f s" % (duration_ns / 1_000_000_000)
        end
      end
      module_function :humanize_duration
    end
  end

  extend Benchmark::IPS # make ips/ips_quick available as module-level method

  ##
  # :singleton-method: ips
  #
  #     require 'benchmark/ips'
  #
  #     Benchmark.ips do |x|
  #       # Configure the number of seconds used during
  #       # the warmup phase (default 2) and calculation phase (default 5)
  #       x.config(:time => 5, :warmup => 2)
  #
  #       # These parameters can also be configured this way
  #       x.time = 5
  #       x.warmup = 2
  #
  #       # Typical mode, runs the block as many times as it can
  #       x.report("addition") { 1 + 2 }
  #
  #       # To reduce overhead, the number of iterations is passed in
  #       # and the block must run the code the specific number of times.
  #       # Used for when the workload is very small and any overhead
  #       # introduces incorrectable errors.
  #       x.report("addition2") do |times|
  #         i = 0
  #         while i < times
  #           1 + 2
  #           i += 1
  #         end
  #       end
  #
  #       # To reduce overhead even more, grafts the code given into
  #       # the loop that performs the iterations internally to reduce
  #       # overhead. Typically not needed, use the |times| form instead.
  #       x.report("addition3", "1 + 2")
  #
  #       # Really long labels should be formatted correctly
  #       x.report("addition-test-long-label") { 1 + 2 }
  #
  #       # Compare the iterations per second of the various reports!
  #       x.compare!
  #     end
  #
  # This will generate the following report:
  #
  #     Calculating -------------------------------------
  #                 addition    71.254k i/100ms
  #                addition2    68.658k i/100ms
  #                addition3    83.079k i/100ms
  #     addition-test-long-label
  #                             70.129k i/100ms
  #     -------------------------------------------------
  #                 addition     4.955M (± 8.7%) i/s -     24.155M
  #                addition2    24.011M (± 9.5%) i/s -    114.246M
  #                addition3    23.958M (±10.1%) i/s -    115.064M
  #     addition-test-long-label
  #                              5.014M (± 9.1%) i/s -     24.545M
  #
  #     Comparison:
  #                addition2: 24011974.8 i/s
  #                addition3: 23958619.8 i/s - 1.00x slower
  #     addition-test-long-label:  5014756.0 i/s - 4.79x slower
  #                 addition:  4955278.9 i/s - 4.85x slower
  #
  # See also Benchmark::IPS
end