Nathan
Nathan

Reputation: 301

Ruby: What is the speed difference between While and Until loops?

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

Answers (3)

Jon
Jon

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

m. simon borg
m. simon borg

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

Zonet
Zonet

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

Related Questions