views:

1501

answers:

8

Ruby is slow at certain things. But what parts of it are the most problematic?

How much does the garbage collector affect performance? I know I've had times when running the garbage collector alone took several seconds, especially when working with OpenGL libraries.

I've used matrix math libraries with Ruby that were particularly slow. Is there an issue with how ruby implements basic math?

Are there any dynamic features in Ruby that simply cannot be implemented efficiently? If so, how do other languages like Lua and Python solve these problems?

Has there been recent work that has significantly improved performance?

+7  A: 

The most problematic part is "everyone".

Bonus points if that "everyone" didn't really use the language, ever.

Seriously, 1.9 is much faster and now is on par with python, and jruby is faster than jython.

Garbage collectors are everywhere; for example, Java has one, and it's faster than C++ on dynamic memory handling. Ruby isn't suited well for number crunching; but few languages are, so if you have computational-intensive parts in your program in any language, you better rewrite them in C (Java is fast with math due to its primitive types, but it paid dearly for them, they're clearly #1 in ugliest parts of the language).

As for dynamic features: they aren't fast, but code without them in static languages can be even slower; for example, java would use a XML config instead of Ruby using a DSL; and it would likely be SLOWER since XML parsing is costly.

alamar
No no, I like having a garbage collector. I was just wondering if ruby's garbage collector implementation could be improved. And by dynamic features, I was wondering if anything made ruby more complex to optimize than Python or Lua. 1.9 breaks compatibility with 1.8 -- is there a lot of library support for it yet?
Nick Retallack
Also that bit about "everyone" is totally not what I meant. I've removed it from the question. I've used ruby a lot. It just seems like, despite implementing some things efficiently (symbols for example) there are a lot of slower things (like inject).
Nick Retallack
Well, is your code slow? If it is, did you profile it?Everything can be improved, but jruby is faster than jython (and on par with python and other C-based languages) yet is compatible with 1.8.
alamar
@alamar python is not a C-based language--it's implemention is, though, if that's what you mean.
rogerdpack
+3  A: 

Ruby 1.9.1 is about twice as fast as PHP, and a little bit faster than Perl, according to some benchmarks.

(Update: My source is this (screenshot). I don't know what his source is, though.)

Ruby is not slow. The old 1.8 is, but the current Ruby isn't.

August Lilleaas
"according to some benchmarks" -- Not the ones I've seen. :)
musicfreak
http://mwrc2009.confreaks.com/13-mar-2009-11-55-the-great-rails-refactor-yehuda-katz.html. Screenshot of the part where he compares performance: http://img.skitch.com/20090622-f5gkwpjy3umbpn3gp8rtt8afsu.jpg
August Lilleaas
+3  A: 

Steve Dekorte: "Writing a Mandelbrot set calculator in a high level language is like trying to run the Indy 500 in a bus."

http://www.dekorte.com/blog/blog.cgi?do=item&id=4047

I recommend to learn various tools in order to use the right tool for the job. Doing matrix transformations could be done efficiently using high-level API which wraps around tight loops with arithmetic-intensive computations. See RubyInline gem for an example of embedding C or C++ code into Ruby script.

There is also Io language which is much slower than Ruby, but it efficiently renders movies in Pixar and outperforms raw C on vector arithmetics by using SIMD acceleration.

Oleg Andreev
+6  A: 

Ruby is very good for delivering solutions quickly. Less so for delivering quick solutions. It depends what kind of problem you're trying to solve. I'm reminded of the discussions on the old CompuServe MSBASIC forum in the early 90s: when asked which was faster for Windows development, VB or C, the usual answer was "VB, by about 6 months".

In its MRI 1.8 form, Ruby is - relatively - slow to perform some types of computationally-intensive tasks. Pretty much any interpreted language suffers in that way in comparison to most mainstream compiled languages.

The reasons are several: some fairly easily addressable (the primitive garbage collection in 1.8, for example), some less so.

1.9 addresses some of the issues, although it's probably going to be some time before it becomes generally available. Some of the other implementation that target pre-existing runtimes, JRuby, IronRuby, MagLev for example, have the potential to be significantly quicker.

Regarding mathematical performance, I wouldn't be surprised to see fairly slow throughput: it's part of the price you pay for arbitrary precision. Again, pick your problem. I've solved 70+ of the Project Euler problems in Ruby with almost no solution taking more than a mintue to run. How fast do you need it to run and how soon do you need it?

Mike Woodhouse
I agree. If performance is an issue, you're using the wrong tool for the job.
mikek
+3  A: 

I assume that you're asking, "what particular techniques in Ruby tend to be slow."

One is object instantiation. If you are doing large amounts of it, you want to look at (reasonable) ways of reducing that, such as using the flyweight pattern, even if memory usage is not a problem. In one library where I reworked it not to be creating a lot of very similar objects over and over again, I doubled the overall speed of the library.

Curt Sampson
+6  A: 

Hmm - I worked on a project a few years ago where I scraped the barrel with Ruby performance, and I'm not sure much has changed since. Right now it's caveat emptor - you have to know not to do certain things, and frankly games / realtime applications would be one of them (since you mention OpenGL).

The culprit for killing interactive performance is the garbage collector - others here mention that Java and other environments have garbage collection too, but Ruby's has to stop the world to run. That is to say, it has to stop running your program, scan through every register and memory pointer from scratch, mark the memory that's still in use, and free the rest. The process can't be interrupted while this happens, and as you might have noticed, it can take hundreds of milliseconds.

Its frequency and length of execution is proportional to the number of objects you create and destroy, but unless you disable it altogether, you have no control. My experience was there were several unsatisfactory strategies to smooth out my Ruby animation loop:

  • GC.disable / GC.enable around critical animation loops and maybe an opportunistic GC.start to force it to go when it can't do any harm. (because my target platform at the time was a 64MB Windows NT machine, this caused the system to run out of memory occasionally. But fundamentally it's a bad idea - unless you can pre-calculate how much memory you might need before doing this, you're risking memory exhaustion)
  • Reduce the number of objects you create so the GC has less work to do (reduces the frequency / length of its execution)
  • Rewrite your animation loop in C (a cop-out, but the one I went with!)

These days I would probably also see if JRuby would work as an alternative runtime, as I believe it relies on Java's more sophisticated garbage collector.

The other major performance issue I've found is basic I/O when trying to write a TFTP server in Ruby a while back (yeah I pick all the best languages for my performance-critical projects this was was just an experiment). The absolute simplest tightest loop to simply respond to one UDP packet with another, contaning the next piece of a file, must have been about 20x slower than the stock C version. I suspect there might have been some improvements to make there based around using low-level IO (sysread etc.) but the slowness might just be in the fact there is no low-level byte data type - every little read is copied out into a String. This is just speculation though, I didn't take this project much further but it warned me off relying on snappy I/O.

The main speed recent increase that has gone on, though I'm not fully up-to-date here, is that the virtual machine implementation was redone for 1.9, resulting in faster code execution. However I don't think the GC has changed, and I'm pretty sure there's nothing new on the I/O front. But I'm not fully up-to-date on bleeding-edge Ruby so someone else might want to chip in here.

Matthew Bloch
Very good answer! If I could select two answers, I'd choose you too. Yes, it was probably silly of me to try to build realtime graphical applications in Ruby, and I ended up disabling and manually triggering the GC between frames to make it at least animate somewhat smoothly, if not very slowly. I had better luck with Python -- particularly with Pyglet, an amazing little opengl library that's far more useful than SDL, which everyone seems to be binding to these days.
Nick Retallack
+10  A: 

Ruby is slow. But what parts of it are the most problematic?

It does "late lookup" for methods, to allow for flexibility. This slows it down quite a bit. It also has to remember names per context to allow for eval, so its frames and method calls are slower.

How much does the garbage collector effect performance? I know I've had times when running the garbage collector alone took several seconds, especially when working with OpenGL libraries.

from some of the graphs at http://www.igvita.com/2009/06/13/profiling-ruby-with-googles-perftools/ I'd say it takes about 10% which is quite a bit--you can decrease that hit by increasing the malloc_limit in gc.c and recompiling.

I've used matrix math libraries with Ruby that were particularly slow. Is there an issue with how ruby implements basic math?

Ruby 1.8 "didn't" implement basic math it implemented Numeric classes and you'd call things like Fixnum#+ Fixnum#/ once per call--which was slow. Ruby 1.9 cheats a bit by inlining some of the basic math ops.

Are there any dynamic features in Ruby that simply cannot be implemented efficiently? If so, how do other languages like Lua and Python solve these problems?

Things like eval are hard to implement efficiently, though much work can be done, I'm sure. The kicker for Ruby is that it has to accomodate for somebody in another thread changing the definition of a class spontaneously, so it has to be very conservative.

Has there been recent work that has significantly improved performance?

1.9 is like a 2x speedup. It's also more space efficient. JRuby is constantly trying to improve speed-wise [and probably spends less time in the GC than KRI]. Besides that I'm not aware of much except little hobby things I've been working on. Note also that 1.9's strings are at times slower because of encoding friendliness.

Cheers! =r

rogerdpack
Finally, a real answer! Too much evangelism in this thread. I was most interested in hearing about ambitious language features that make it difficult to optimize compared to other dynamic languages. It never occurred to me that you could have concurrency issues with redefining classes at runtime. That's good that 1.9 has improved at basic math -- I'll have to try it now.I wish ruby programmers didn't use eval so much, but sometimes I stumble across a class that's half string literal with interpolation. It just seems wrong.
Nick Retallack