What is an idiomatic way to measure time in Ruby? - ruby

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.

Related

Why do these two methods give me two different times?

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

How to pass a parameter to a method that is also a parameter itself in Ruby?

*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

Benchmarking Ruby Code

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.

When is the Enumerator::Yielder#yield method useful?

The question "Meaning of the word yield" mentions the Enumerator::Yielder#yield method. I haven't used it before, and wonder under what circumstances it would be useful.
Is it mainly useful when you want to create an infinite list of items, such as the Sieve of Eratosthenes, and when you need to use an external iterator?
"How to create an infinite enumerable of Times?" talks about constructing and lazy iterators, but my favorite usage is wrapping an existing Enumerable with additional functionality (any enumerable, without needing to know what it really is, whether it's infinite or not etc).
A trivial example would be implementing the each_with_index method (or, more generally, with_index method):
module Enumerable
def my_with_index
Enumerator.new do |yielder|
i = 0
self.each do |e|
yielder.yield e, i
i += 1
end
end
end
def my_each_with_index
self.my_with_index.each do |e, i|
yield e, i
end
end
end
[:foo, :bar, :baz].my_each_with_index do |e,i|
puts "#{i}: #{e}"
end
#=>0: foo
#=>1: bar
#=>2: baz
Extending to something not already implemented in the core library, such as cyclically assigning value from a given array to each enumerable element (say, for coloring table rows):
module Enumerable
def with_cycle values
Enumerator.new do |yielder|
self.each do |e|
v = values.shift
yielder.yield e, v
values.push v
end
end
end
end
p (1..10).with_cycle([:red, :green, :blue]).to_a # works with any Enumerable, such as Range
#=>[[1, :red], [2, :green], [3, :blue], [4, :red], [5, :green], [6, :blue], [7, :red], [8, :green], [9, :blue], [10, :red]]
The whole point is that these methods return an Enumerator, which you then combine with the usual Enumerable methods, such as select, map, inject etc.
For example you can use it to construct Rack response bodies inline, without creating classes. An Enumerator can also work "outside-in" - you call Enumerator#each which calls next on the enumerator and returns every value in sequence. For example, you can make a Rack response body returning a sequence of numbers:
run ->(env) {
body = Enumerator.new do |y|
9.times { |i| y.yield(i.to_s) }
end
[200, {'Content-Length' => '9'}, body]
}
Since Mladen mentioned getting other answers, I thought I would give an example of something I just did earlier today while writing an application that will receive data from multiple physical devices, analyze the data, and connect related data (that we see from multiple devices). This is a long-running application, and if I never threw away data (say, at least a day old with no updates), then it would grow infinitely large.
In the past, I would have done something like this:
delete_old_stuff if rand(300) == 0
and accomplish this using random numbers. However, this is not purely deterministic. I know that it will run approximately once every 300 evaluations (i.e. seconds), but it won't be exactly once every 300 times.
What I wrote up earlier looks like this:
counter = Enumerator.new do |y|
a = (0..300)
loop do
a.each do |b|
y.yield b
end
delete_old_stuff
end
end
and I can replace delete_old_stuff if rand(300) == 0 with counter.next
Now, I'm sure there is a more efficient or pre-made way of doing this, but being sparked to play with Enumerator::Yielder#yield by your question and the linked question, this is what I came up with.
It seems to be useful when you have multiple objects you want to enumerate over, but flat_map isn't suitable, and you want to chain the enumeration with another action:
module Enumerable
def count_by
items_grouped_by_criteria = group_by {|object| yield object}
counts = items_grouped_by_criteria.map{|key, array| [key, array.length]}
Hash[counts]
end
end
def calculate_letter_frequencies
each_letter.count_by {|letter| letter}
end
def each_letter
filenames = ["doc/Quickstart", "doc/Coding style"]
# Joining the text of each file into a single string would be memory-intensive
enumerator = Enumerator.new do |yielder|
filenames.each do |filename|
text = File.read(filename)
text.chars.each {|letter| yielder.yield(letter)}
end
end
enumerator
end
calculate_letter_frequencies

please help with my "shuffle" code in ruby

this is the question
Shuffle. Now that you’ve finished your
new sorting algorithm, how about the
opposite? Write a shuffle method that
takes an array and returns a totally
shuffled version. As always, you’ll
want to test it, but testing this one
is trickier: How can you test to make
sure you are getting a perfect
shuffle? What would you even say a
perfect shuffle would be? Now test for
it.
This is my code answer:
def shuffle arr
x = arr.length
while x != 0
new_arr = []
rand_arr = (rand(x))
x--
new_arr.push rand_arr
arr.pop rand_arr
end
new_arr
end
puts (shuffle ([1,2,3]))
What are my mistakes? Why doesn't this code work?
Here's a far more Rubyish version:
class Array
def shuffle!
size.downto(1) { |n| push delete_at(rand(n)) }
self
end
end
puts [1,2,3].shuffle!
Here's a more concise way of writing it:
def shuffle(arr)
new_arr = []
while (arr.any?) do
new_arr << arr.delete_at(rand(arr.length))
end
new_arr
end
And some tests:
5.times do
puts shuffle((1..5).to_a).join(',')
end
>> 4,2,1,3,5
>> 3,2,1,4,5
>> 4,2,5,1,3
>> 5,2,1,4,3
>> 4,3,1,5,2
Beside minor other errors you seems not to understand what pop and push are doing (taking or adding some items from the end of the array).
You are probably trying to write something like below.
def shuffle arr
x = arr.length
new_arr = []
while x != 0
randpos = rand(x)
x = x-1
item = arr[randpos]
new_arr.push item
arr[randpos] = arr[x]
arr.pop
end
new_arr
end
puts (shuffle ([1,2,3]))
You're getting your indexes mixed up with your values. When you do new_arr.push rand_arr, you're putting whatever random index you came up with as a value on the end of new_arr. What you meant to do is new_arr.push arr[rand_arr], where arr[rand_arr] is the value at the index rand_arr in arr.
Ruby 1.8.7 and 1.9.2 have a built-in Array#shuffle method.
A variant of Mark Thomas's answer. His algorithm can be quite slow with a large array, due to delete operation performance.
class Array
def shuffle!
size.downto(1) do |n|
index=rand(n)
# swap elements at index and the end
self[index], self[size-1] = self[size-1],self[index]
end
self
end
end
puts [1,2,3].shuffle!
This algorithm is O(size), while Mark's algorithm is O(size^2). On my computer, Mark's answer takes 400 seconds to shuffle an array of 1,000,000 elements on my machine, versus 0.5 seconds with my method.

Resources