Reputation: 301
I'm learning Ruby right now. Coming from using Javascript the past couple of years, I'm familiar with the While loop. But the until loop? I've looked around but couldn't find a solid reason why one would be better than the other.
Ruby has "until" which is described as another way to phrase a problem. The way I see it, "while" iterates until false, and "until" iterates until true.
I'm sure that most of the programs I write won't really need refactoring for speed. However, I like to get into the little details sometimes.
Is there a speed difference between the two loops? Why is there an "until" syntax in Ruby? Why not just stick with "while?"
Upvotes: 2
Views: 368
Reputation: 2980
There would not be a speed difference between while
and until
as they mirror each other.
We'll compare a while
loop with an until
loop:
n = 0
puts n += 1 while n != 3
n = 0
puts n += 1 until n == 3
These will both print 1 through 3.
Here's a diff between the two disassembled human-readable instruction sequences from the Ruby VM:
@@ -13,7 +13,7 @@
0021 pop
0022 getlocal_OP__WC__0 2
0024 putobject 3
-0026 opt_neq <callinfo!mid:!=, argc:1, ARGS_SIMPLE>, <callcache>, <callinfo!mid:==, argc:1, ARGS_SIMPLE>, <callcache>
-0031 branchif 8
-0033 putnil
-0034 leave
+0026 opt_eq <callinfo!mid:==, argc:1, ARGS_SIMPLE>, <callcache>
+0029 branchunless 8
+0031 putnil
+0032 leave
A while
loop uses a branchif
for its jump, whereas the until
loop used a branchunless
. So, these loops simply differ in the comparison being made, which you can see by looking at how branchif
and branchunless
are defined:
DEFINE_INSN
branchif
(OFFSET dst)
(VALUE val)
()
{
if (RTEST(val)) {
RUBY_VM_CHECK_INTS(th);
JUMP(dst);
}
}
DEFINE_INSN
branchunless
(OFFSET dst)
(VALUE val)
()
{
if (!RTEST(val)) {
RUBY_VM_CHECK_INTS(th);
JUMP(dst);
}
}
Performance between while
and until
should be nearly identical. Usage should be determined by readability.
Upvotes: 5
Reputation: 2575
Speed will be more influenced by your choice of comparison operator than your choice of while
or until
Benchmark.bmbm do |bm|
bm.report('while') do
n = 0
n += 1 while n != 10_000_000
end
bm.report('until') do
n = 0
n += 1 until n == 10_000_000
end
end
user system total real
while 0.250000 0.000000 0.250000 ( 0.247949)
until 0.220000 0.000000 0.220000 ( 0.222049)
With while n != 10_000_000
vs. until n == 10_000_000
, until
appears to be faster.
Benchmark.bmbm do |bm|
bm.report('while') do
n = 0
n += 1 while n < 10_000_000
end
bm.report('until') do
n = 0
n += 1 until n == 10_000_000
end
end
user system total real
while 0.210000 0.000000 0.210000 ( 0.207265)
until 0.220000 0.000000 0.220000 ( 0.223195)
Change it to while n < 10_000_000
and now while
seems to have the edge. To be fair we should give them the more equivalent while n < 10_000_000
vs. until n > 9_999_999
Benchmark.bmbm do |bm|
bm.report('while') do
n = 0
n += 1 while n < 10_000_000
end
bm.report('until') do
n = 0
n += 1 until n > 9_999_999
end
end
user system total real
while 0.200000 0.000000 0.200000 ( 0.208428)
until 0.200000 0.000000 0.200000 ( 0.206218)
Now they're almost identical. So follow Ruby's lead and gain your satisfaction from code that reads like an English sentence. But make sure you use <
or >
to gain that extra boost of .0000000001 seconds.
Upvotes: 3
Reputation: 302
Speed differences aside, it's really all about readability, which is something that Ruby prides itself on.
Let's pretend we're making a drink - which do you think reads better?
A) pour_drink until glass.full?
B) pour_drink while !glass.full?
Upvotes: 3