Why Is Ruby's Loop Command Slower Than While True

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

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.

Is each slower than while in Ruby?

When you do range=range.reject {..}, you don't modify the parent range (which you shouldn't do, because it would mess up the iteration--you need reject! to do that) but rather construct a temporary array which only gets assigned to the parent range variable at the end of the iteration.

The each iteration in prime2 runs over the whole original range, not the shortened which, before the loop ends, only exist in the block.

The while version modifies the original array and is therefore quicker (BTW, you realize that i remains zero and it's the range.count that changes (decreases) in that while condition and that reject iterates over the WHOLE array again--even the beginning where there couldn't possibly be any more nonprimes to reject).

You'll get a much faster result if you improve the logic of your code. That array manipulation is costly and for this, you don't even need an array:

def prime3?(prime_candidate)
return false if prime_candidate==1
return true if prime_candidate==2
range = 2..(Math.sqrt(prime_candidate).ceil)
range.all? {|x| prime_candidate % x !=0 }
end #about 300× times as fast for your example as prime1? on my PC

Ruby - I have code that I like but I would like it to loop until one of the conditions is true

Put an infinite loop around the code using loop operator. Put next after the "bad" condition to go back to the beginning of the loop. In all other cases, the code falls through to the final break operator before the end of the infinite loop, which causes the loop to exit. If you do not need to reuse math variable, you can get rid of it altogether. Here it is used only once, so I remove it, and instead gets.chomp.downcase is directly evaluated in the case statement without the need of a temporary variable.

#!/usr/bin/env ruby

puts "Welcome to math.rb!"
puts "Enter a number!"
user = gets.to_i
puts "Enter another number!"
user2 = gets.to_i
puts "What would you like to do with your number?"

loop do
case gets.chomp.downcase
when "add"
puts user + user2
when "subtract"
puts user - user2
when "multiply"
puts user * user2
else
puts "I don't understand! Type a command like:
-add
-subtract
-multiply"
next
end
break
end

SEE ALSO:

loop is the most accepted and common way to create infinite loops in Ruby : Creating an Infinite Loop

loop is a kernel method which takes a block, which introduces a new local variable scope in the infinite loop. This is unlike while true infinite loop, which does not introduce a new scope. Thus, while true may leak variables outside of the loop, which may be unexpected and lead to subtle bugs later: https://stackoverflow.com/a/45070639/967621

Ruby while loop with sleep

The console output is buffered and nobody promised to IO#flush it for you:

$i = 0
$num = 3

while $i < $num do
$stdout.puts "My loop just executed"
$stdout.flush
sleep 10
$i +=1
end

Why do people say that Ruby is slow?

Why is Ruby considered slow?

Because if you run typical benchmarks between Ruby and other languages, Ruby loses.

I do not find Ruby to be slow but then
again, I'm just using it to make
simple CRUD apps and company blogs.
What sort of projects would I need to
be doing before I find Ruby becoming
slow? Or is this slowness just
something that affects all programming
languages?

Ruby probably wouldn't serve you well in writing a real-time digital signal processing application, or any kind of real-time control system. Ruby (with today's VMs) would probably choke on a resource-constrained computer such as smartphones.

Remember that a lot of the processing on your web applications is actually done by software developed in C. e.g. Apache, Thin, Nginx, SQLite, MySQL, PostgreSQL, many parsing libraries, RMagick, TCP/IP, etc are C programs used by Ruby. Ruby provides the glue and the business logic.

What are your options as a Ruby
programmer if you want to deal with
this "slowness"?

Switch to a faster language. But that carries a cost. It is a cost that may be worth it. But for most web applications, language choice is not a relevant factor because there is just not enough traffic justify using a faster language that costs much more to develop for.

Which version of Ruby would best suit
an application like Stack Overflow
where speed is critical and traffic is
intense?

Other folks have answered this - JRuby, IronRuby, REE will make the Ruby part of your application run faster on platforms that can afford the VMs. And since it is often not Ruby that causes slowness, but your computer system architecture and application architecture, you can do stuff like database replication, multiple application servers, loadbalancing with reverse proxies, HTTP caching, memcache, Ajax, client-side caching, etc. None of this stuff is Ruby.

Finally, I can't find much news on
Ruby 2.0 - I take it we're a good few
years away from that then?

Most folks are waiting for Ruby 1.9.1. I myself am waiting for Rails 3.1 on Ruby 1.9.1 on JRuby.

Finally, please remember that a lot of developers choose Ruby because it makes programming a more joyful experience compared to other languages, and because Ruby with Rails enables skilled web developers to develop applications very quickly.

How come this algorithm in Ruby runs faster than in Parallel'd C#?

Actually, the bug is quite subtle, and has nothing to do with threading. The reason that your C# version takes so long is that the intermediate values computed by the collatz method eventually start to overflow the int type, resulting in negative numbers which may then take ages to converge.

This first happens when i is 134,379, for which the 129th term (assuming one-based counting) is 2,482,111,348. This exceeds the maximum value of 2,147,483,647 and therefore gets stored as -1,812,855,948.

To get good performance (and correct results) on the C# version, just change:

int current = i;

…to:

long current = i;

…and:

static int collatz(int num)

…to:

static long collatz(long num)

That will bring down your performance to a respectable 1.5 seconds.

Edit: CodesInChaos raises a very valid point about enabling overflow checking when debugging math-oriented applications. Doing so would have allowed the bug to be immediately identified, since the runtime would throw an OverflowException.

Overflow checking

OverflowException

When implementing an infinite loop, is there a difference in using while(1) vs for(;;) vs goto (in C)?

They are equivalent, even if you turn the optimizer off.

Example:

#include <stdio.h>

extern void f(void) {
while(1) {
putchar(' ');
}
}

extern void g(void) {
for(;;){
putchar(' ');
}
}

extern void h(void) {
z:
putchar(' ');
goto z;
}

Compile with gcc -O0 gives equivalent assembly for all 3 functions:

 f:
; [ EXTERNAL ]
;
+00000 00000fb4 80402DE9 stmdb sp!,{r7,lr}
+00004 00000fb8 00708DE2 add r7,sp,#0x0
+00008 00000fbc 2000A0E3 loc_000008: mov r0,#0x20
+0000c 00000fc0 0A0000EB bl putchar (stub)
+00010 00000fc4 FCFFFFEA b loc_000008
;
;
g:
; [ EXTERNAL ]
;
+00000 00000fc8 80402DE9 stmdb sp!,{r7,lr}
+00004 00000fcc 00708DE2 add r7,sp,#0x0
+00008 00000fd0 2000A0E3 loc_000008: mov r0,#0x20
+0000c 00000fd4 050000EB bl putchar (stub)
+00010 00000fd8 FCFFFFEA b loc_000008
;
;
h:
; [ EXTERNAL ]
;
+00000 00000fdc 80402DE9 stmdb sp!,{r7,lr}
+00004 00000fe0 00708DE2 add r7,sp,#0x0
+00008 00000fe4 2000A0E3 loc_000008: mov r0,#0x20
+0000c 00000fe8 000000EB bl putchar (stub)
+00010 00000fec FCFFFFEA b loc_000008


Related Topics



Leave a reply



Submit