I'm doing the following Ruby Tutorial https://rubymonk.com/learning/books/4-ruby-primer-ascent/chapters/50-debugging/lessons/124-benchmarking_ruby_code. One of the exercises asks me to:
use Ruby's super-awesome blocks to create a method which takes in a
block, executes it, and returns the time it took.
The exercise looks like this:
def benchmark
# your code here!
end
time_taken = benchmark do
sleep 0.1
end
puts "Time taken #{time_taken}"
there is a hint (Need a hint?) below the exercise:
Ruby Blocks - Introduction to Blocks in Ruby (Ruby Primer)
and i did so:
def benchmark(time)
begin_time = Time.now
end_time = Time.now
time.benchmark {|time| yield time}
end
time_taken = benchmark do
sleep 0.1
end
puts "Time taken #{time_taken}
but received an error.
i am interested in: why is local variable - 'time_taken', suggested without representing an element after 'do'? or is it not necessary? Can anyone tell me how to write code to get the positive result.
You should do it much easier:
def benchmark
begin_time = Time.now
yield
end_time = Time.now
end_time - begin_time
end
time_taken = benchmark do
sleep 0.1
end
puts "Time taken #{time_taken}"
First you collect the time and store in in variable begin_time, then yield - so run the block, then collect the end time. Return the difference. That's it.
That's pretty far off, and not really at all salvagible.
Your benchmark method should look like this pseudo code:
def benchmark
let begin_time -> current time
execute the block
let end_time -> current_time
return endtime - begintime
end
As far as executing the block being passed in, there is no time.benchmark method, I'm not sure where that came from, and you do not need to pass anything into the block. You want a single, simple yield.
Related
Both methods calculate the time it takes ruby to call and run a code block. I don't see any reason why these two methods should return different results.
methodone gives me: 1.000135157
methodtwo gives me: 1.000108267
I'm a noob, am I even doing this right? please let me know.
def methodone(a)
start = Time.now
a.call
result = Time.now - start
end
def methodtwo(a)
start_time = Time.now
a.call
end_time = Time.now
result = end_time - start_time
end
a = Proc.new do {}
end
p methodone(a)
p methodtwo(a)
You don't get the same output always because the CPU of your machine can be less or more used by other processes running on your computer as well as some caching and interpreter optimizations can occur. For such simple methods you can't reliably time them by just a single pass. If you want to benchmark something like that it is better to tun it thousands or millions of times and then take an average. This will produce a more consistent result, because the "noise" of outside factors gets canceled out.
You shouldn't expect them to be exactly the same. There will always be something going on outside of the Ruby process that will impact performance. You should consider a margin of error of, say, 0.1%
def time(&block)
t = Time.now.to_f
yield
t2 = Time.now.to_f
puts t2 - t
end
50.times do
time do
Proc.new { }
end
end
This is pretty ugly:
t = Time.now
result = do_something
elapsed = Time.now - t
I tried this:
elapsed = time do
result = do_something
end
def time
t = Time.now
yield
Time.now - t
end
This is better. But the problem is that result falls out of scope after the block ends.
So, is there a better way of doing timing? Or a good way to use the result?
A really idiomatic way would be to use the standard library. :)
require 'benchmark'
result = nil
elapsed = Benchmark.realtime do
result = do_something
end
You've got the right idea here, but to avoid the scope problem do this:
result = nil
elapsed = time do
result = do_something
end
I like the way you've constructed your time method. I have no suggestions for improvement, but I will say a few words about a related problem. Suppose you wished to measure the amount of time spent executing methods. Sometimes you might be able to write something simple such as:
require 'time'
t = Time.now
rv = my_method(*args)
et = t.Time.now - t
Other times that's not convenient. Suppose, for example, you were constructing an array whose elements were the return values of my_method or my_method returned an enumerator so that it could be chained to other methods.
As an example, let's suppose you wanted to sum the values of an array until a zero is encountered. One way to do that is to construct an enumerator stop_at_zero that generates values from its receiver until it encounters a zero, then stops (i.e., raises a StopIteration exception). We could then write:
arr.stop_at_zero.reduce(:+)
If we want to know how much time is spent executing stop_at_zero we could construct it as follows.
class Array
def stop_at_zero
extime = Time.now
Enumerator.new do |y|
begin
each do |n|
sleep(0.5)
return y if n.zero?
y << n
end
ensure
$timings << [__method__, Time.now - extime]
end
end
end
end
I used a begin, ensure, end block to make sure $timings << [__method__, Time.now - extime] is executed when the method returns prematurely. sleep(0.5) is of course just for illustrative purposes.
Let's try it.
$timings = []
arr = [1,7,0,3,4]
arr.stop_at_zero.reduce(:+)
#=> 8
$timings
#=> [[:stop_at_zero, 1.505672]]
$timings will contain a history of execution times of all methods that contain the timing code.
*Apologies if the question's wording is confusing. I didn't know exactly how to ask it.
How can I do something like this?
def track_time(function, input)
beg = Time.now
function(input)
end = Time.now
end - beg
end
And then pass it a function and a value for that function to use.
def double(value)
value + value
end
p track_time(double, 5)
The goal is to create something repeatable so I can track how long different functions take to complete.
First you can not use 'end' as a variable name.
As for your question, I agree with Mladen Jablanovićyou that for this use case a block is better, but since you specifically asked about passing a method as a parameter to another method, you can use the 'send' method:
def track_time method, value
begin_time = Time.now
send method, value
end_time = Time.now
end_time - begin_time
end
def double(value)
value + value
end
p trcak_time(:double, 5)
Unfortunately, methods in Ruby are not first-class objects, so they can't be directly passed as arguments. You can pass a name of the method (usually passed as symbol) instead, as other answers suggest.
But the idiomatic way to achieve what you are aiming for are blocks:
def track_time
start = Time.now
yield
finish = Time.now
finish - start
end
track_time do
double(5)
end
#=> 6.127e-06
Remember that end is a reserved word in Ruby (I suspect it was for illustration purposes anyhow).
You could pass in the string/symbol of the function name instead.
def track_time(function, input)
start = Time.now
method(function).call(input)
finish = Time.now
finish - start
end
def double(value)
value + value
end
track_time('double', 5)
=> 6.127e-06
How would I do a specific task every x amount of seconds in ruby? I've tried using Time.now.to_i for epoch then once a Time.now_i hits that task second it executes, but I have not successfuly done this, can someone show me a small example on how to execute a function every x amount of seconds?
Attempt:
def interval(timeout,function,*data)
now = Time.now.to_i
tasktime = Time.now.to_i + timeout
taskfunction = function
taskdata = data
end
I stopped the code there because I do not know how/what to do next in ruby, so what it should do for example if someone can generate a code that can do something like this example,
def say(word)
puts word
end
If you set a interval for the function would be say, the data would be the "word" then it would execute that function every x amount of seconds
If you simply sleep for a constant amount of time as suggested in other answers, the error will contaminate as it keeps running, and will not be accurate. In fact, each iteration would take longer than the given interval.
The answer shown below adjusts the lag each time per iteration.
module Kernel
def tick_every sec, &pr
Thread.new do loop do
pr.call
t = Time.now.to_f
frac = t.modulo(sec.to_f)
sleep(sec - frac)
end end
end
end
thread = tick_every(2) do
puts "foo"
end
...
some_other_tasks
...
thread.kill
You can use Kernel#sleep method for the same.
Here is the post
Ruby sleep or delay less than a second?
Tell Ruby Program to Wait some amount of time
This method would puts the word every 2 seconds endless, synchronously (means other ruby code has to wait until this execution is finished (..endless..:)).
def say(word)
while true do
t = Time.now.to_f
puts word
frac = t.modulo(2.to_f)
sleep(2 - frac)
end
end
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!
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)
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.
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