Hacker News new | ask | show | jobs
by vinceguidry 4672 days ago
I'm putting my faith in Ruby. It might take 10 years, but eventually the performance will resemble C's. It's basically a compile-to-C language right now as it is. There's just a whole bunch of inefficiencies in the implementation. Once they get ironed out, we'll finally be able to have our cake and eat it too. One language to rule them all.
6 comments

No it won't. There are so many problems with trying to get a dynamic language like ruby to be in the same order of magnitude of performance as C.

Garbage collection, inlining virtual calls etc.

Go has garbage collection and it's in the same order of magnitude as C. In fact so are Scala and Java. So, we can at least stop blaming gc.
Yes, but neither language is nearly as dynamic as ruby. That's where the costs come in. Method calls in ruby are comparatively glacial in speed.
That's why the ten years.
after ten years, square wheels will suddenly be round?
If you've been rotating them against a surface in that time period; then they'll certainly be a lot more round than when they started.
"Just a whole bunch of inefficiencies in the implementation."

That's a common misconception: what makes a language "faster" than another is not only more or less optimizations in the compiler or efficiencies in the runtime. There are language features that just kill performance. The typical culprits, in descending order of cost:

- dynamic typing

- dynamic dispatch (virtual methods)

- mandated bounds checking

- existence of an "eval" function

- mandated introspection

To write fast code, you have to use the right data structures (impossible if everything is a hash map), manage memory (possible even with garbage collection), and be able to inline everything in the inner loops (introspection,eval,etc make this hard).
In case you haven't seen this Charles Nutter post from a few months back, you may find it interesting. The TL;DR is that dynamic features in languages come at a cost (but he says "prove me wrong").

http://blog.headius.com/2013/05/on-languages-vms-optimizatio...

> It's basically a compile-to-C language right now as it is.

No, it's actually not even close. The things that make Ruby slower than C are not just "implementation issues".

It's presumably at least fairly close since RubyMotion compiles down to executables?

There's bits of Ruby that aren't implemented (I believe some due to iOS, some due to difficulty?) but what am I missing about the "compile-to-C"?

Well, the compile-to-C thing is one thing. It's possible to compile any language down to C. You could compile Brainfuck or Python down to C if you wanted. The question is how complicated the resultant code will end up being compared to pure C. I would only call Ruby a "compile-to-C" language if it could generate code that is at least somewhat comparable to sane C code. Here's some C code that sums all the integers in an array:

    long sum(int *a, int len)
    {
        long ret = 0;
        for (int i = 0; i < len; i++)
            ret += a[i];
        return ret;
    }
The entire loop (the conditional test, the increment, and the `ret` update) could probably be implemented in less than 10 native instructions depending on your machine. Faaaaast. If Ruby were a compile-to-C language, I would expect it to produce C code that looked somewhat like this. So let's look at the same snippet in Ruby:

    # sum the first len elements of a
    def sum(a, len)
      ret = i = 0
      while i < len
        ret += a[i]
        i += 1
      end
      ret
    end
(This is far from being idiomatic Ruby code, but this solution is the simplest and it also seems like it would be the easiest to directly translate to C.) Semantically, here's what that would translate to (in a C-like pseudocode):

    RubyObject *sum(RubyObject *a, RubyObject *len)
    {
         RubyObject *ret = newRubyInteger(0);
         RubyObject *i = newRubyInteger(0);
         while (call(getMethod(i, "<"), len)) {
             ret = call(getMethod(ret, "+"), call(getMethod(a, "[]"), i));
             i = call(getMethod(i, "+"), 1)
         }
         return ret;
    }
Why is this so complicated? Because I've captured Ruby's dynamic typing and dynamic dispatch within the function itself. `ret` isn't a long, it's a Ruby variable that can hold any type of object, so we need to capture that in the source. Same with `i`, `a`, and `len`. When we say `a[i]`, we're not jumping to the `i`th element of the integer array `a`, which would be super fast. Instead, we have to dynamically dispatch the `[]` method, which will perform bounds checking and a bunch of type-checking. We also have to dynamically dispatch the `<` and `+` methods everywhere, which perform type-checking themselves. Obviously, this all takes much, much more than 10 native instructions. You can't generally optimize out the method dispatches, since you are generally allowed in Ruby to redefine methods of built-in classes wherever you want. You might be able to perform some static analysis to get rid of some dynamic types, but you have to be careful with machine integers, since they overflow without warning. You'd have to check after every operation you do that the operation didn't overflow, and switch it out for a big integer if that happens. Any of these methods could raise exceptions and that's a nontrivial problem to deal with. The garbage collector is also running in the background.

And this is just a simple example, too. Things get a hell of a lot more complicated when you introduce blocks and dynamic scoping (which I purposefully stayed away from). So that should paint a somewhat clear picture of why it's not just an issue of waiting 10 years until Ruby gets as fast as C. I don't know how close it's even possible to get without messing with the semantics of the language.

Ah, I see what you mean now. Ta.
Well, Rubinius is quite faster than MRI Ruby (still not as fast as C). Although, I don't like your long prognosis, I love the attitude!

However, I have never seen the problem in writing C extensions for Ruby when I am pressed for speed. Only complaint that I can think of is that my first extension was "problematic" in terms of not being able to find any resources, which made me resort to reading the source, which in turn made me a better Rubyist!

I think what's ultimately going to be needed is better profiling tools and an easy-to-use extension DSL that's designed to hook into Ruby. That way we could experiment with different structures than hash maps and still keep good Ruby style.
No, Ruby has too many difficulties. LuaJIT is closest to C performance right now, there are innumerable discussions about why Ruby/Python are much harder/impossible to do this for.