Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 479
Florian Pilz Avatar asked Jun 17 '11 05:06

Florian Pilz


2 Answers

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)
like image 78
Florian Pilz Avatar answered Nov 07 '22 09:11

Florian Pilz


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.

like image 28
Mat Avatar answered Nov 07 '22 09:11

Mat