6

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!

Florian Pilz
  • 8,002
  • 4
  • 22
  • 30

3 Answers3

9

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)
Florian Pilz
  • 8,002
  • 4
  • 22
  • 30
  • I just wish there was the shortcut for `require 'benchmark'; Benchmark.measure { ... }` – Ev Dolzhenko Dec 25 '13 at 18:57
  • I don't understand, the quoted part is pretty short. What would you like to have? (Pseduo-Code allowed ;) – Florian Pilz Dec 28 '13 at 23:23
  • sure, probably can't get shorter than this, was just lamenting the lack of some shortcut provided with ruby by default to save typing. + I always forget is it `measure` or `bm` or `bmbm` or something else :) – Ev Dolzhenko Dec 30 '13 at 13:47
2

You could use the Process::times function, which returns user time/system time. (It does not report wall clock time, you'll need something else for that). Seems to be a bit version or OS dependent though.

This is what it reports on my system (linux, ruby 1.8.7):

$ irb
irb(main):001:0> t = Process.times
=> #<struct Struct::Tms utime=0.01, stime=0.0, cutime=0.0, cstime=0.0>

The docs show this though, so some versions/implementations might only have the first two:

t = Process.times
[ t.utime, t.stime ]   #=> [0.0, 0.02]

See times for the underlying call on Linux.

Here's a really crappy wrapper that kind of supports -:

class SysTimes

    attr_accessor :user, :system

    def initialize
        times = Process.times
        @user = times.utime
        @system = times.stime
    end

    def -(other)
        diff = SysTimes.new
        diff.user = @user - other.user
        diff.system = @system - other.system
        diff
    end
end

Should give you ideas to make it work nicely in your context.

Mat
  • 202,337
  • 40
  • 393
  • 406
  • Sadly it's a struct which doesn't know how to respond to `-`, thus `time = Process.times ; user, system = Process.times - time` will raise an error. However, didn't know about this method before and I like the approach, since I may look up how long my program is running in any point of time. :) Thanks for your contribution! – Florian Pilz Jun 17 '11 at 05:59
0

This gem might help: https://github.com/igorkasyanchuk/benchmark_methods

No more code like this:

t = Time.now
user.calculate_report
puts Time.now - t

Now you can do:

benchmark :calculate_report # in class

And just call your method

user.calculate_report
Igor Kasyanchuk
  • 766
  • 6
  • 12