A Simple Ruby Script Freezes My Machine - ruby

CONSTANT = 1000000000000000000
array = (1..CONSTANT).to_a
start = Time.now
array.each do |i|
if 1000 < i < CONSTANT * 9 / 10
elsif i > CONSTANT * 9 / 10
else
end
end
finish = Time.now
puts "Running time: #{finish - start} seconds"
I wrote the above script in an attempt to figure out how much time saving could be achieved by re-ordering the control branches. The script froze my machine immediately after being run, which couldn't be terminated via CTL + C.
Could someone please point out what happened there?

You're iterating over a huge number.
Lets say your CPU can iterate one array item per nanosecond (which is good!), that means it'll take 1e9 seconds to iterate over the array. That's 31 years!
Your constant is far, far too large.

You are creating a huge array which you don't even need. That is forcing your machine to start paging (it would take up 5% of the 64-bit address space and your machine certainly doesn't have that much RAM). Your code is not making it to the loop (the condition in the if statement isn't valid ruby).
CONSTANT = 1000
# start with something small, then increase by 10
# 10000000 works for me.
start = Time.now
CONSTANT.times do |i|
if 1000 < i && CONSTANT * 9 / 10
elsif i > CONSTANT * 9 / 10
else
end
end
finish = Time.now
puts "Running time: #{finish - start} seconds"

What happened is that you tried to create a rather large array:
CONSTANT = 1000000000000000000
array = (1..CONSTANT).to_a
And your machine got upset with you. You might have better luck using the Range on its own without the to_a call.

Related

How to call a method every X seconds in Rails? [duplicate]

For example, if I want to make a timer, how do I make a delay in the loop so it counts in seconds and do not just loop through it in a millisecond?
The 'comment' above is your answer, given the very simple direct question you have asked:
1.upto(5) do |n|
puts n
sleep 1 # second
end
It may be that you want to run a method periodically, without blocking the rest of your code. In this case, you want to use a Thread (and possibly create a mutex to ensure that two pieces of code are not attempting to modify the same data structure at the same time):
require 'thread'
items = []
one_at_a_time = Mutex.new
# Show the values every 5 seconds
Thread.new do
loop do
one_at_a_time.synchronize do
puts "Items are now: #{items.inspect}"
sleep 5
end
end
end
1000.times do
one_at_a_time.synchronize do
new_items = fetch_items_from_web
a.concat( new_items )
end
end
Somehow, many people think that putting a sleep method with a constant time interval as its argument will work. However, note that no method takes zero time. If you put sleep(1) within a loop, the cycle will surely be more than 1 second as long as you have some other content in the loop. What is worse, it does not always take the same time processing each iteration of a loop. Each cycle will take more than 1 second, with the error being random. As the loop keeps running, this error will contaminate and grow always toward positive. Especially if you want a timer, where the cycle is important, you do not want to do that.
The correct way to loop with constant specified time interval is to do it like this:
loop do
t = Time.now
#... content of the loop
sleep(t + 1 - Time.now)
end

How to put a delay on a loop in Ruby?

For example, if I want to make a timer, how do I make a delay in the loop so it counts in seconds and do not just loop through it in a millisecond?
The 'comment' above is your answer, given the very simple direct question you have asked:
1.upto(5) do |n|
puts n
sleep 1 # second
end
It may be that you want to run a method periodically, without blocking the rest of your code. In this case, you want to use a Thread (and possibly create a mutex to ensure that two pieces of code are not attempting to modify the same data structure at the same time):
require 'thread'
items = []
one_at_a_time = Mutex.new
# Show the values every 5 seconds
Thread.new do
loop do
one_at_a_time.synchronize do
puts "Items are now: #{items.inspect}"
sleep 5
end
end
end
1000.times do
one_at_a_time.synchronize do
new_items = fetch_items_from_web
a.concat( new_items )
end
end
Somehow, many people think that putting a sleep method with a constant time interval as its argument will work. However, note that no method takes zero time. If you put sleep(1) within a loop, the cycle will surely be more than 1 second as long as you have some other content in the loop. What is worse, it does not always take the same time processing each iteration of a loop. Each cycle will take more than 1 second, with the error being random. As the loop keeps running, this error will contaminate and grow always toward positive. Especially if you want a timer, where the cycle is important, you do not want to do that.
The correct way to loop with constant specified time interval is to do it like this:
loop do
t = Time.now
#... content of the loop
sleep(t + 1 - Time.now)
end

Ruby programming double a penny a day for period of time

I am new, three wekks into Ruby as my first Language. here is the code I already have:
=begin
this program is to calculate doubling a penny a day for thirty one days
=end
puts "how many days would you like to calculate"
days = gets.chomp.to_i
i = 1
loop do
puts "#{i}"
break i >=100
end
I have tried to use ** as this is they syntax for exponential use. I have considered an until loop also, but the thing I am having most difficulty with is how to double per day each integer for given time.
I have also tried "#{i**2}" , "#{i**i}" , I have tried to google this problem for the past 2 days, to no avail.
It can be done using a simple bit shifting operation. Binary value "1" shifted left n times is used to calculate 2^n.
puts "how many days would you like to calculate"
days = gets.chomp.to_i
puts 1 << (days - 1)
You don't need any loop here. What about a power? If you want to double 1 penny in 31 days, you need to calculate 2^30:
puts "how many days would you like to calculate"
days = gets.chomp.to_i
puts 2 ** (days - 1)
Try:
# Display the question to the user in the terminal
puts 'How many days would you like to calculate?'
# Get the number of days from stdin
days = gets.chomp.to_i
# starting at 1 and up to the number of days start doubling. Reduce returns the result back to itself, thus doubling the return of each number until you have reached the up to limit.
result = 1.upto(days).reduce { |start| start * 2 }
# Put the result
puts result
31.times.reduce 1 do |a| a * 2 end
#=> 2147483648

Ruby/Rspec Performance Monitor

So I'm working on Rspec problems, and this is the last one I have left. For whatever reason, it's been much harder than all of the others. The three Rspec tests that are in question are as folows:
it "runs a block N times" do
n = 0
measure(4) do
n += 1
end
n.should == 4
end
it "returns the average time, not the total time, when running multiple times" do
run_times = [8,6,5,7]
fake_time = #eleven_am
Time.stub(:now) { fake_time }
average_time = measure(4) do
fake_time += run_times.pop
end
average_time.should == 6.5
end
it "returns the average time when running a random number of times for random lengths of time" do
fake_time = #eleven_am
Time.stub(:now) { fake_time }
number_of_times = rand(10) + 2
average_time = measure(number_of_times) do
delay = rand(10)
fake_time += delay
end
average_time.should == (fake_time - #eleven_am).to_f/number_of_times
end
And my code is as follows:
require 'time'
def measure(pass = 0)
start_time = Time.now
if pass == 0
yield
else
pass.times {|current| result = yield(current)}
end
Time.now - start_time
end
(The if/else is present, as an earlier test requires that the code takes one second to execute a program that sleeps for 1 second. In that case, pass would be 0, so the program would jump straight to the yield.)
Full Rspec here
Now, the code DOES pass the 'it "runs a block N times" do' test, but I feel that the way I have it set up prevents the other two tests from being able to pass. (At the same time, a simple yield won't allow it to pass, because it will get an error from trying to + 1 to nil)
I'm not looking for a copy/paste answer, but moreso whether or not I'm on the right track (Or if my pass.times should be reworked.)
If you have any examples that may be able to lead me in the right direction, I'd be more than happy to see them!
You say you "feel" it won't let the other tests pass, do you know this? I just ran it, and here's the results:
Performance Monitor
takes about 0 seconds to run an empty block
takes exactly 0 seconds to run an empty block (with stubs)
takes about 1 second to run a block that sleeps for 1 second
takes exactly 1 second to run a block that sleeps for 1 second (with stubs)
runs a block N times
returns the average time, not the total time, when running multiple times (FAILED - 1)
So the last spec fails. Looks like it's just returning the wrong value, it doesn't look at whether there were multiple passes. So updating that:
require 'time'
def measure(pass = 0)
start_time = Time.now
if pass == 0
yield
else
pass.times {|current| result = yield(current)}
end
(Time.now - start_time) / (pass == 0 ? 1 : pass)
end
Now running the specs shows me:
(in /Users/nick/learn_ruby)
Performance Monitor
takes about 0 seconds to run an empty block
takes exactly 0 seconds to run an empty block (with stubs)
takes about 1 second to run a block that sleeps for 1 second
takes exactly 1 second to run a block that sleeps for 1 second (with stubs)
runs a block N times
returns the average time, not the total time, when running multiple times
returns the average time when running a random number of times for random lengths of time
Finished in 1.01 seconds
7 examples, 0 failures
Great part about testing first is you can just find out if what you are doing is wrong, and then fix it.
def measure(pass = 1)
start_time = Time.now
pass.times {yield}
(Time.now - start_time) / (pass)
end
for any future passerbys, this could be written more efficiently like so, using a default value of 1 for pass

ping function every x amount seconds

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

Resources