Performance: Ruby vs Java

I’m a newcomer to Ruby, and thought I would write a little
brute-force-search program to see how Ruby compares to Java -
performance wise. The results surprised me (I was hoping Ruby would
fare a lot better than it did!). On my WinXP box (AthlonXP 1800+,
768Meg RAM), the Ruby program takes 1min 50secs to complete, while a
similar java program takes 0.6 secs.

Here are the programs:

Ruby (ruby 1.8.0 (2003-08-04) [i386-mswin32]):
def isPrime(n)
for i in 1…n-1 do
for j in 1…n-1 do
if i * j == n then
return false
end
end
end
return true
end

puts isPrime(2)
puts isPrime(4)
puts isPrime(7)
puts isPrime(25)
puts isPrime(400003)

Java (HotSpot™ Client VM (build 1.4.2_01-b06, mixed mode)):
public class Prime
{
public static void main(String[] args)
{
Prime p = new Prime();
System.out.println(p.isPrime(2));
System.out.println(p.isPrime(4));
System.out.println(p.isPrime(7));
System.out.println(p.isPrime(25));
System.out.println(p.isPrime(400003));
}

private boolean isPrime(int n)
{
for (int i = 1; i < n; i++)
{
for (int j = 1; j < n; j++)
{
if (i*j == n)
{
return false;
}
}
}
return true;
}
}

Kindly note that the isPrime() method above is not meant to be the
most efficient way of finding primes!

Any ideas on why i’m seeing such a big difference in performance?

The difference is that ruby is interpreted and has a dynamic nature that
makes it difficult to optimize (or so I understand), while java is jitted
and for this single non-OO example it is probably as fast as C.

I can’t resist the temptation of plugging my own project rubydotnet, which
allows you to implement such performance critical code in a .net language.

CS_SCRIPT = <<-EOF
int n = (int) args[0];
for (int i=0; i<n-1; ++i)
for (int j=0; j<n-1; ++j)
if (j*i == n)
return false;
return true;
EOF

def isPrime(n)
cs_eval(CS_SCRIPT, n)
end

puts isPrime(2)
puts isPrime(4)
puts isPrime(7)
puts isPrime(25)
puts isPrime(400003)

On my laptop (3ghz P4):
$ time ruby prime.rb
true
false
true
false
false

real 0m0.666s
user 0m0.015s
sys 0m0.000s

Neat, huh?

Cheers,

Thomas

“Lalit Pant” lalit_pant@yahoo.com wrote in message
news:f260fa27.0309211647.3bb8bc95@posting.google.com

···

I’m a newcomer to Ruby, and thought I would write a little
brute-force-search program to see how Ruby compares to Java -
performance wise. The results surprised me (I was hoping Ruby would
fare a lot better than it did!). On my WinXP box (AthlonXP 1800+,
768Meg RAM), the Ruby program takes 1min 50secs to complete, while a
similar java program takes 0.6 secs.

Here are the programs:

Ruby (ruby 1.8.0 (2003-08-04) [i386-mswin32]):
def isPrime(n)
for i in 1…n-1 do
for j in 1…n-1 do
if i * j == n then
return false
end
end
end
return true
end

puts isPrime(2)
puts isPrime(4)
puts isPrime(7)
puts isPrime(25)
puts isPrime(400003)

Java (HotSpot™ Client VM (build 1.4.2_01-b06, mixed mode)):
public class Prime
{
public static void main(String args)
{
Prime p = new Prime();
System.out.println(p.isPrime(2));
System.out.println(p.isPrime(4));
System.out.println(p.isPrime(7));
System.out.println(p.isPrime(25));
System.out.println(p.isPrime(400003));
}

private boolean isPrime(int n)
{
for (int i = 1; i < n; i++)
{
for (int j = 1; j < n; j++)
{
if (i*j == n)
{
return false;
}
}
}
return true;
}
}

Kindly note that the isPrime() method above is not meant to be the
most efficient way of finding primes!

Any ideas on why i’m seeing such a big difference in performance?

Lalit Pant wrote:

I’m a newcomer to Ruby, and thought I would write a little
brute-force-search program to see how Ruby compares to Java -
performance wise. The results surprised me (I was hoping Ruby would
fare a lot better than it did!). On my WinXP box (AthlonXP 1800+,
768Meg RAM), the Ruby program takes 1min 50secs to complete, while a
similar java program takes 0.6 secs.

[snip]

This must be a JIT issue, since the difference is so big.

I compared the Ruby version to the C version (below) and got
a comparable ratio. I bet you will, too.

Bottom line: Ruby is slower because it doesn’t do any JIT.

Other factors might be the lack of bytecode compilation and
the startup time of the interpreter (though Java has that,
too).

Some JIT implementations are pretty good. I once had to do a
little demonstration to prove to my officemate that a program
(similar to this one) actually ran faster in Java than in C.
Of course, that’s highly dependent on the compilers, the
nature of the code, your default optimizations, and the phase
of the moon.

Hal

Lalit Pant wrote:

I’m a newcomer to Ruby, and thought I would write a little
brute-force-search program to see how Ruby compares to Java -
performance wise. The results surprised me (I was hoping Ruby would
fare a lot better than it did!). On my WinXP box (AthlonXP 1800+,
768Meg RAM), the Ruby program takes 1min 50secs to complete, while a
similar java program takes 0.6 secs.

[programs snipped]

Any ideas on why i’m seeing such a big difference in performance?

File this explanation under “duh”, but an obvious big reason for the
difference would be the difference in runtimes. Java’s HotSpot (at some
point) compiles Java bytecodes into native code, so at some point your
Java program will be compiled to the equivalent of a C program.

On the other hand, Ruby, as I understand it, executes a program by
walking over its parse tree, in the classic Gang-of-Four Interpreter
pattern. Even compared to pure bytecode interpreters, tree-walking
tends to be inefficient, especially for nested loops running over a
large number of integers. (A bytecode interpreter is planned, but
apparently still just a twinkle in Matz’s eye.)

For raw number-crunching, you probably wouldn’t want to use Ruby (or
Python, or Perl, or Tcl). Ruby and similar languages can match Java
speeds in other tasks, notably when the script spends most of its time
using builtin objects implemented in C.

You may be interested in a comparison of C, C++, Java, Perl, Python,
Rexx, and Tcl:
http://wwwipd.ira.uka.de/~prechelt/Biblio/jccpprt_computer2000.pdf
Obviously it doesn’t refer directly to Ruby, but many observations from
the latter four languages apply to Ruby.

···


Frank Mitchell (frankm each bayarea period net)

Please avoid sending me Word or PowerPoint attachments.
See We Can Put an End to Word Attachments - GNU Project - Free Software Foundation

I’m a newcomer to Ruby, and thought I would write a little
brute-force-search program to see how Ruby compares to Java -
performance wise. The results surprised me (I was hoping Ruby would
fare a lot better than it did!). On my WinXP box (AthlonXP 1800+,
768Meg RAM), the Ruby program takes 1min 50secs to complete, while a
similar java program takes 0.6 secs.

Just a note/thought: I once implemented a similar program in several
programming languages. When it came to ruby, my conclusion was to rather
use “Narray” for code like this. Narray is a ruby extension written in
C, which makes it sufficiently fast. It requires a small modification to
the algorithm though.

Cheers,
Thomas.

Hello Lalit,

Monday, September 22, 2003, 1:48:09 AM, you wrote:

I’m a newcomer to Ruby, and thought I would write a little
brute-force-search program to see how Ruby compares to Java -
performance wise. The results surprised me (I was hoping Ruby would
fare a lot better than it did!). On my WinXP box (AthlonXP 1800+,
768Meg RAM), the Ruby program takes 1min 50secs to complete, while a
similar java program takes 0.6 secs.

Ruby is the wrong language for programs like this. A speed difference
of 18000% (or 180 times) is not unusual if you try to port algorithms on large data
sets with lots of low level calculations.

Writing parsers (that can’t use regular expressions) or numerical
computing are the best visible sign that ruby is dynamic and untyped.

By the way. Common Lisp will not be much slower then Java, if used
with type declarations. There is a lot of possible optimizations, but
common lisp costs around 5000$ a license. I doubt that ruby has more
then a handfull of users who wants to pay this.

···


Best regards,
Lothar mailto:mailinglists@scriptolutions.com

BTW, just for comparison I translated your program into Python and Lua.
(See www.python.org and www.lua.org, respectively.) Both languages
compile their scripts into bytecodes before running them.

On my pathetic 166 MHz laptop running Linux, calculating whether 400003
was prime took too long, so I knocked out a couple of zeroes. These are
the timings I ended up with:

prime.lua: 0:57.96 # Lua 5.0
prime.py: 3:23.01 # Python 2.2.2
prime.rb: 4:50.35 # Ruby 1.8.0
Prime.java 0:08.01 # Kaffe 1.0.7, JIT enabled

So, even with Kaffe, the JIT makes one or two orders of magnitude
difference. (The Lua number shows what a small and ultra-optimized
interpreter could do.)

Attached are the Python and Lua translations.

prime.py (281 Bytes)

prime.lua (316 Bytes)

···


Frank Mitchell (frankm each bayarea period net)

Please avoid sending me Word or PowerPoint attachments.
See http://www.fsf.org/philosophy/no-word-attachments.html

He said “brute force”…
the algo, mathematically should stop at sqrt(n)…

···

Thomas Link samul@web.de wrote:

It requires a small modification to
the algorithm though.


Yvon

Thanks a lot for the feedback, guys. I’ll get on with the job of
learning/having fun with Ruby. Seems like a fascinating language…

No. Anyone can write a Ruby bytecode interpereter.

:wink: :wink:

···

Dave Brown (dagbrown@LART.ca) wrote:

Isn’t a Ruby bytecode interpreter kind of dependent on the Parrot
guys getting their bit done first?


Eric Hodel - drbrain@segment7.net - http://segment7.net
All messages signed with fingerprint:
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

Dave Brown wrote:

In article 3F6E5631.F7C124A8@bayFNORDarea.net,
: On the other hand, Ruby, as I understand it, executes a program by
: walking over its parse tree, in the classic Gang-of-Four Interpreter
: pattern. Even compared to pure bytecode interpreters, tree-walking
: tends to be inefficient, especially for nested loops running over a
: large number of integers. (A bytecode interpreter is planned, but
: apparently still just a twinkle in Matz’s eye.)

Isn’t a Ruby bytecode interpreter kind of dependent on the Parrot
guys getting their bit done first?

No, because Matz wants his own bytecode set. Generating Parrot
bytecodes is a separate issue.

Hal

···

Frank Mitchell frankm@bayFNORDarea.net wrote:

“Thomas Sondergaard” thomas@FirstNameGoesHereSondergaard.com wrote in message news:3f6e50b5$0$48907$edfadb0f@dtext02.news.tele.dk

The difference is that ruby is interpreted and has a dynamic nature that
makes it difficult to optimize (or so I understand), while java is jitted
and for this single non-OO example it is probably as fast as C.

I’ve said this before, and I think it’s important to say it again: the
performance difference between Java and Ruby has everything to do
with the JIT and nothing to do with Ruby’s “dynamic nature”, or the
fact that Java is statically typed.

This is easy to demonstrate with Smalltalk, an equally dynamic
language that has JITted implementations. My point here isn’t to plug
Smalltalk (who, me?), but to drive home my point about JIT compilation
vs. static typing.

On my machine (Athlon 1400), Ruby takes 121.969s to try that algorithm
on 400003. VisualWorks Smalltalk takes 2.158 seconds. That’s a 60x
speedup, without any ugly primitive types or type declarations.

Of course, a good interpeter can help a lot too. Squeak Smalltalk,
which is a straight bytecode interpreter with no JIT, manages it in
16.147s - still almost an order of magnitude faster than Ruby.

By the way, the code I used in Smalltalk was

isPrime
1 to: self - 1 do:
[:i |
1 to: self - 1 do:
[:j |
i*j = self ifTrue: [^ false]]].
^ true

Although this is idiomatic Smalltalk (in the same way that the Java
code presented was idiomatic Java), we can actually make it closer to
the Ruby version by creating range objects and iterating over them:

isPrime
(1 to: self - 1) do:
[:i |
(1 to: self - 1) do:
[:j |
i*j = self ifTrue: [^ false]]].
^ true

In both Squeak and VisualWorks this roughly doubles the times.

In article 3f6e50b5$0$48907$edfadb0f@dtext02.news.tele.dk,

···

Thomas Sondergaard thomas@FirstNameGoesHereSondergaard.com wrote:

The difference is that ruby is interpreted and has a dynamic nature that
makes it difficult to optimize (or so I understand), while java is jitted
and for this single non-OO example it is probably as fast as C.

I can’t resist the temptation of plugging my own project rubydotnet, which
allows you to implement such performance critical code in a .net language.

CS_SCRIPT = <<-EOF
int n = (int) args[0];
for (int i=0; i<n-1; ++i)
for (int j=0; j<n-1; ++j)
if (j*i == n)
return false;
return true;
EOF

def isPrime(n)
cs_eval(CS_SCRIPT, n)
end

puts isPrime(2)
puts isPrime(4)
puts isPrime(7)
puts isPrime(25)
puts isPrime(400003)

On my laptop (3ghz P4):
$ time ruby prime.rb
true
false
true
false
false

real 0m0.666s
user 0m0.015s
sys 0m0.000s

Or, for those of us not on a .NET platform (Windows) or those of us who
are wary of it…

You could do something very similar using Ryan Davis’ RubyInline package.

Phil

In article 41137062.20020219123425@scriptolutions.com,

Hello Lalit,

Monday, September 22, 2003, 1:48:09 AM, you wrote:

I’m a newcomer to Ruby, and thought I would write a little
brute-force-search program to see how Ruby compares to Java -
performance wise. The results surprised me (I was hoping Ruby would
fare a lot better than it did!). On my WinXP box (AthlonXP 1800+,
768Meg RAM), the Ruby program takes 1min 50secs to complete, while a
similar java program takes 0.6 secs.

Ruby is the wrong language for programs like this. A speed difference
of 18000% (or 180 times) is not unusual if you try to port algorithms on
large data
sets with lots of low level calculations.

But that doesn’t mean you can’t use Ruby on the project… How much code
is really speed critical? It’s going to vary depending on what
type of project you’re doing, but in most cases it’s probably 10% or less.

Since it’s easy to extend Ruby with C/C++ code, Ruby can still be a very
good choice for a project which requires high performance in some areas of
the code. Tools like swig (http://www.swig.org), rubyinline
(RubyInline download | SourceForge.net), and cgenerator
(CGenerator) make it even easier to create
C/C++ extensions for code that is performance sensitive.

Phil

···

Lothar Scholz mailinglists@scriptolutions.com wrote:

Huh?

Perhaps there’s an implementation of Common Lisp that costs $5000 a
license, but there’s several free (as in speech) implementations
availible. I’m just playing around with CL, so I don’t know which one is
the “best” but the one I’m using to toy around with CL is CMUCL. (Sorry,
I don’t have a URL. Google for it.)

Jason Creighton

···

On Mon, 22 Sep 2003 19:34:28 +0900 Lothar Scholz mailinglists@scriptolutions.com wrote:

By the way. Common Lisp will not be much slower then Java, if used
with type declarations. There is a lot of possible optimizations, but
common lisp costs around 5000$ a license. I doubt that ruby has more
then a handfull of users who wants to pay this.

“Lothar Scholz” mailinglists@scriptolutions.com schrieb im Newsbeitrag
news:41137062.20020219123425@scriptolutions.com

By the way. Common Lisp will not be much slower then Java, if used
with type declarations. There is a lot of possible optimizations, but
common lisp costs around 5000$ a license. I doubt that ruby has more
then a handfull of users who wants to pay this.

Lisp can even be faster than Java:

The article about the original study - if you’re an ACM member

Regards

robert

(I just can’t let this go …)

Frank Mitchell wrote:

(The Lua number shows what a small and ultra-optimized
interpreter could do.)

Immediately after posting, I realized the Lua instruction

for i = a,b do ... end

is specifically for iterating over numbers from a to b. Ruby and Python
only iterate over sequences of object instances (including integers),
created lazily. That may explain part of the 3-5x speed difference.

···


Frank Mitchell (frankm each bayarea period net)

Please avoid sending me Word or PowerPoint attachments.
See We Can Put an End to Word Attachments - GNU Project - Free Software Foundation