Search code examples
rubytimebenchmarkingmeasurementsystemtime

Measure user time or system time in Ruby without Benchmark or time


Since I'm doing some time measurements at the moment, I wondered if it is possible to measure the user time or system time without using the Benchmark class or the command line utility time.

Using the Time class only reveals the wall clock time, not system and user time, however I'm looking for a solution which has the same flexibility, e.g.

time = TimeUtility.now
# some code
user, system, real = TimeUtility.now - time

The reason is that I somehow dislike Benchmark, since it cannot return numbers only (EDIT: I was wrong - it can. See answers below.). Sure, I could parse the output, but that doesn't feels right. The time utility from *NIX systems should solve my problem as well, but I wanted to know if there already is some kind of wrapper implemented in Ruby so I don't need to make these system calls by myself.

Thanks a lot!


Solution

  • I re-read the Benchmark documentation and saw that it has a method named measure. This method does exactly what I want: Measure the time your code needs and returning an object which contains user time, system time, system time of childrens etc. It is as easy as

    require 'benchmark'
    measurement = Benchmark.measure do
      # your code goes here
    end
    

    In the process I found out that you can add custom rows to the Benchmark output. You can use this to get the best of both worlds (custom time measurements and a nice output at the end) as follows:

    require 'benchmark'
    
    measurements = []
    10.times { measurements << Benchmark.measure { 1_000_000.times { a = "1" } } }
    
    # measurements.sum or measurements.inject(0){...} does not work, since the
    # array contains Benchmark instances, which cannot be coerced into Fixnum's
    # Array#sum will work if you are using Rails
    sum = measurements.inject(nil) { |sum, t| sum.nil? ? sum = t : sum += t }
    avg = sum / measurements.size
    
    # 7 is the width reserved for the description "sum:" and "avg:"
    Benchmark.bm(7, "sum:", "avg:") do |b|
      [sum, avg]
    end
    

    The result will look like the following:

                 user     system      total        real
    sum:     2.700000   0.000000   2.700000 (  2.706234)
    avg:     0.270000   0.000000   0.270000 (  0.270623)