Ruby 3x3: Matz, Koichi, and Tenderlove on the Future of Ruby Performance
blog.heroku.com
blog.heroku.com
It's become a horrible meme I can run a script that reformats a 30 line CSV file the way I want it in 0.05s and someone will almost certainly lean over and say "Ruby is too slow, you really need to upgrade to <x>". Or the intranet application I wrote in Sinatra last year for a total of four users, where all I got questions about how it could be usable without "asynchronous".
That said, I don't understand Ruby's performance position. IBM released a preview of their JIT implementation over a year ago, with much fanfare. By many accounts, it held major practical improvements. Since then, I haven't heard a thing about it, and the Ruby team seem to be spending years talking about implementing the same thing.
Web apps do drive ruby's adoption, though. Now that I know ruby I use it for many things, but I learned it initially for rails. Many other languages (like python) can fulfill ruby's scripting usages (I would argue javascript can't - despite its many strengths it's just a little too awkward)
I like the standard library and I love the gems that fill the gaps the stdlib shouldn't really fill.
Many devs I know reach for the language they use day to day and their scripts and productivity suffer. Ruby is almost like an openly secret weapon in that sense.
If Ruby can offer: - real threads - great performance
Then the conversation about Ruby would be about the right things - do you like the syntax, the feel, would it work for your team, etc. We wouldn't be bickering about "performance" all the time.
Disclaimer: I'm a Ruby developer.
Making Ruby 3 times faster is not going to save it. It's just way, way behind the raw performance of other programming languages. Also, with the arrival of new, modern programming languages, there isn't that many productivity reasons anymore to justify the poor performance and lack of sane concurrency.
I love Ruby, the programming language, but I kind of start seeing that others haven't been idle and there are some growing ecosystems that can soon leave Ruby behind. It kind of feels like Perl back in the day: it was still a good programming language when it started losing ground.
We, Rubyists, have seen a lot of talented people leave both the Ruby and Rails core teams. You see them go to other, younger communities, making them bloom. In the meantime, the Ruby ecosystem seems stale and lacking fresh ideas.
The other problem is that there is not a single company that will throw as much money as it takes to improve the language, as happens with JavaScript, Python or PHP.
I am not trying to say that Ruby or Rails are a thing of the past and shouldn't be used. They are still great tools and put in the right hands can give your company / project huge productivity and more-than-acceptable performance. But there is a time for everything, and I think that the golden years of Ruby are gone.
I don't know if this is really the reason I believe Ruby hasn't evolved as much as other languages. There are no big entities pushing big on Haskell, for example (Facebook uses it, but you don't see them driving development), but GHC evolves like crazy to support more language features, be better optimized, etc..
Isn't it fair to say that a community that loves to say "X isn't <good attribute> but it doesn't need to be!" is just showing disinterest in evolving? There will always be a point if you pat yourself on the back too much about your one big advantage, where you find yourself with increasingly less of an advantage. Your disinterest in improving caused you to stagnate and suddenly there is no advantage anymore.
At some point relative properties like "It has a very broad ecosystem" and "It's the most concise language" don't turn out to be very big advantages or no longer be true, in the case of the latter. There are other examples of this in the clique formerly known as "scripting languages". Most of them can be beaten by implementing new languages on platforms that started with less ideal languages.
The same criticism could be leveled at Python with regard to the GIL and JIT (or lack-thereof). CPython lacks these mechanisms because Guido prefers to keep the official implementation simple. When it comes down to it, a language is based on what maintainers want to do.
Absolutely. Python lacks many things and what I wrote previously is exactly what I think about Python. There are lots of flaws and the supposed advantages of Python are getting less meaningful by the month. It only gets worse when you consider the big part of the community that refuses to actually move forward. In that sense, they're worse than the Perl community, because at least the Perl programmers had to wait an eternity for their new and improved language.
Adding something akin to Guilds it could give it a nice advantage if implemented nicely.
>the Ruby ecosystem seems stale and lacking fresh ideas
I think that's mostly because it's not the new shiny toy what it's missing is some sort of cohesion to push it forward.
Maybe, I'm just biased but even with languages like Elixir - I keep coming back to Ruby.
> Making Ruby 3 times faster is not going to save it. It's just way, way behind the raw performance of other programming languages.
Like Python? Ruby is comparable in performance.
> Also, with the arrival of new, modern programming languages, there isn't that many productivity reasons anymore to justify the poor performance and lack of sane concurrency.
There is no other language that makes it as easy to write a DSL. And there are plenty of places to look for concurrency in Ruby, built right into the language. ruby-concurrency provides even more abstractions.
> The other problem is that there is not a single company that will throw as much money as it takes to improve the language, as happens with JavaScript, Python or PHP.
Isn't that company Salesforce/Heroku?
> I am not trying to say that Ruby or Rails are a thing of the past and shouldn't be used. They are still great tools and put in the right hands can give your company / project huge productivity and more-than-acceptable performance. But there is a time for everything, and I think that the golden years of Ruby are gone.
I disagree. There are plenty of projects written in Ruby that are still going strong. The language is just maturing and web dev is not the only thing people do with it anymore.
Only on a cursory glance... python has pypy, cython, greenlets, twisted, numpy/pandas/scipy, and dozens of game engines of varying quality. You can write high quality, high performance python code that fits right in with the rest of the community.
Ruby performance is a joke. It doesn't even ship with a useful memory or performance profiler, hence New Relic's existence. You're correct, it's a great DSL language, and performance shouldn't matter at that level.
Sure, LLVM can literally compile just in time, but JITs need much more than that.
But yeah, their reasoning for avoiding it was a bit strange. Although I understand them when they say that LLVM as a dependency may be to huge for Ruby. LLVM is huge and the bigger project compared to Ruby with major backers (Apple, Google, etc.). What is important for Ruby may not be what has high-priority for the LLVM project. IMHO Ruby is big enough to warrant its own JIT.
The only viable options are dynasm, a simple selfmade jit as in potion/tinyrb or rpython. perl6 went with dynasm.
rujit looked good to me, but the tracing overhead was too much. They should really analyze why it was so big, even as tracing jit.
And the datastructures need to be slimmed down, as in potion/tinyrb or php7 or lua/lisps. Only tagging schemes lead to performance. This has nothing to do with the jit. size matters.
[0] - http://kamome.lib.ynu.ac.jp/dspace/bitstream/10131/9368/1/id...
P.S. this is available via google results, I'm assuming it's public domain
On the other hand, there are projects that do successfully use LLVM for JIT. We now experiment with dynasm, which doesn't optimize, but is fast and much more pleasant to use. libjit looks like another popular option. [1]: http://hhvm.com/blog/10205/llvm-code-generation-in-hhvm
Crystal is neat, but it isn't interesting.
In the middle, there's this exchange:
Jonan: So you would just use pointers to point to those immutable objects?
Koichi: Yes. Also, I want to note that immutable doesn't mean frozen object.
Frozen objects can contain mutable objects. So I mean those immutable objects which only contain children
that point to immutable objects.
Jonan: So if we had a nested hash, some large data structure, we would need to
freeze every object in that in order to reference it this way. Is there a facility in Ruby right now to do that?
I think I would have to iterate over that structure freezing objects manually today.
Matz: Not yet.
Jonan: So there might be?
Matz: We need to provide something to freeze these objects.
Aaron: A deep freeze.
Matz: Yes, deep freeze.
Jonan: Deep Freeze is the name of this feature maybe? I think that would be an excellent name for it.
So yes, they all agree that Ruby's current 'frozen' feature doesn't grant real immutability guarantees.But then at the end...
Jonan: OK. So then, a friend asked me when I described guilds, he writes a lot of Haskell,
he asked me when we are we going to have "real immutable objects", and I don't quite know what he means.
Is there some distinction between an immutable object in Ruby and an
immutable object in a different language that’s important?
Matz: For example in Haskell, everything is immutable, it’s that kind of language, everything is immutable from day one.
Jonan: Yes.
Matz: But in Ruby we have mutable objects, so under that kind of situation we need a whole new construct.
Aaron: Frozen objects should really be immutable. It's really immutable.
Jonan: OK.
Aaron: I don't...
Jonan: You don't know what this person who air-quoted me "real immutable" was saying?
Aaron: Yeah I don't know why they would say "real immutable".
Jonan: Should I unfriend him on Facebook? I think I'm going to after this.
Matz: At least tell him if you want "real immutable" go ahead and use Haskell.
Jonan: I think that's an excellent option, yeah.
Aaron: You just to need to say to them quit "Haskelling" me.
Jonan: I should, I’ll just tell them to quit "Haskelling" me about immutable objects.
I get that they are joking a bit, but do they really not understand that the Haskeller is referring to the lack of deeply immutable objects? The hostility seems a bit weird to me, given that languages like Haskell push forward our understanding of the usefulness of things like immutability, which then get absorbed into other languages (like Ruby, clearly).Ruby is a practical language by design and it's common to see good-natured poking fun at academic concepts that sound good in theory, but fall apart on corner cases (and real projects are full of corner cases).
Case in point: https://twitter.com/dhh/status/216238003817938944
People looking for conceptual purity should look elsewhere.
Ruby has been designed fundamentally as an Alan Kay style object oriented programming language. In that light in my opinion it is more suitable for conceptual purity than most other scripting languages.
DHH's criticism of SRP has to do with legitimate drawbacks. I think all of us have experienced the over-engineering SRP can invoke when applied naievely. Also note that DHH's framework Rails has made significant steps in enabling SRP for modern Rails applications, so this is not a flat out rejection of the idea.
Thankfully it's a very flexible and forgiving language, so there are third-party libraries (e.g. Hashie, ActiveSupport's Concern) to accomplish these kinds of things, but the core language doesn't offer them.
They're after true immutability for threading and optimization reasons. They recognize it has a value. I get the feeling this is them joking about giving their Haskell friends a hard time, in response to said friends giving them a hard time. Some standard friendly back-and-forth tongue-in-cheek language snobbery.
Ruby makes a piss poor Haskell. For one, it's not Haskell - for two, it's not even trying to be Haskell. Even if Ruby fully embraces the joys and wonder of full immutability and pure functional programming now, the cat is out of the bag - there's a ton of mutable procedural impure code out there in Ruby. It simply wasn't built on pure functional programming and immutability as first principles in the same way that Haskell was. Ruby is a language that lets you, at runtime, monkey patch classes with new methods or new variations to old methods - letting you mutate constructs (methods, types, etc.) that in most other languages are immutable after and even during compilation. If you want Ruby to be Haskell, you're going to be disappointed. Fortunately Ruby still makes a pretty good Ruby.
This is in no way saying Haskell is a bad language, just that you should use Haskell if you want Haskell, and Ruby if you want Ruby. This is also in no way saying that Haskell concepts can't be borrowed, stolen, and brought over to Ruby - just that doing so will not turn Ruby into Haskell, no matter how much one might wish it.
I don't really have a problem with the tone of the interview but more with how vague the proposed objectives are.
Also, there seem to be some rather misleading facts in it - like the AOT and the 30% speed-up - in practice, after testing it - it helped to load Rails faster but other than that the benchmarks were virtually identical (don't get me wrong, booting Rails faster is great but they imply something completely different in the interview)
Matz: 30% is huge.
Aaron: Yeah!
Jonan: That seems like a pretty good improvement to me.
Koichi: I do think so.
Aaron: We just need a few more 30% improvements then Ruby 3x3 is done.
Matz: Yeah, that means 30% of the time is spent in the compiler.
EDIT: maybe they were joking once again?
I love working with Ruby; I get lots of work done in very little time. However, sometimes I just can't wrap my head around certain language design, documentation, and implementation decisions.
This is mistake. I said "I don't think so". 30% is not enough.
It grants immutability for the object itself. What they are pointing out is that other than fundamental objects like String, Fixnum etc., those objects will usually contain references to other objects (EDIT: any member variables etc., as well as the class, which means the ojbects observable behaviour can also change even if instance variables don't), and those will still be mutable unless they too are frozen.
So we can do immutability in Ruby currently, it's just a pain to do and requires knowledge about the objects - you could implement a generic "deep_freeze" that iterates over instance variables etc., but it would have uninented consequences because it would know nothing about which object references are shared where it's ok to break that by "dup"'ing the object to freeze it (and without dup'ing it, you risk other code that isn't prepared to work with a frozen object breaking)
This is mostly a "problem" because writing Ruby to work on immutable objects would often result in code that's not idiomatic Ruby, and so you won't get many Ruby developers to agree it's a real concern, though the trend is towards freezing more objects.
Some vague references to the idea of building a JIT are discussed. Rubinius already does this, but they apparently long ago decided that Rubinius doesn't exist, so I guess that's off the table.
I was at the Rubyconf in Denver sitting behind Matz, and watched him fiddle with his computer and tweet instead of paying attention to Brian Ford's well-informed talk on the deep structural problems within the Ruby development community, which included their indifference to concurrency issues https://www.youtube.com/watch?v=BagNfTbXn3w
I then went one of the talks at the same conference on improving Ruby concurrency for IO that was largely unattended because it was scheduled to run at the same time as an Aaron Patterson talk on literally making sausage.
It's clear to me through observation that the Ruby core devs don't take this topic seriously. And while current performance isn't presently a huge problem for my work, it's clear to me that a lot of people are walking away as a consequence of this.
I'm sorry if bringing this up makes me uncool at your parties or whatever, but if we're going to have a real conversation about this, let's have that instead of just singing kumbaya around a dumpster fire. Matz is out there selling people on the idea that there's going to be a 3x performance increase when there obviously isn't going to be one.
And FWIW, I had drinks with Brian, the other concurrency speaker, and I do recall getting tacos and singing some karaoke with Tony Arcieri. You'll have to ask them if I'm good for parties or not.
>[talk on concurrency] that was largely unattended because it was scheduled to run at the same time as an Aaron Patterson talk on literally making sausage
I literally laughed. Such is the live of compiler architects, I guess, they are far and few between.
Then looks at ( all of a sudden ) a few lash out against the Ruby Core Dev.
Those talk were done in 2012. They knew there were problems. But they sat there and did nothing and We are running into 2017.
I am not even sure if Ruby Core Dev talks to IBM OMR team and JRuby team.
Ruby is slow, at least from a Web Development perspective, Rails is VERY slow. So both together combined makes things a little bit bad.
Look at the boring stack from StackExchnage
https://news.ycombinator.com/item?id=12790650
https://twitter.com/Nick_Craver/status/790527231600787456
Any Rails Shop would be happy if they could hit 3x those numbers even with caching.
GitHub does; Tenderlove works on Ruby there!
However, having multiple ruby implementations is great too, and they probably would rather keep working on an independent implementation that they have full control over, and whose goals may not align perfectly with that of other implementations.
For me the slow start-up and the JIT requiring ~50K/req for warm-up just ruin the whole experience in coding the app in Ruby I'd rather just use Elixir.
Another interesting project is IBM's OMR [0] - compared to JRuby there are no slow start-ups and weirdly, being still a dev. preview it can run Rails and its deps. without issues, even better than Rubinius stable.
If we're very lucky, Substrate VM will be released at some point, solving the startup time issues (and I believe many of the JIT issues, though less clear on that): http://lafo.ssw.uni-linz.ac.at/papers/2013_JVMLanguageSummit...
The OMR project definitely looks interesting, but it doesn't seem to be actively developed (or perhaps the most recent commits are just not public yet). But if it works that well, then maybe there's no need, hah. Will have to try it out.
The source code is here: https://github.com/rubyomr-preview/ruby
Seems the code is being developed internally to IBM. First the docker images of the OMR tech preview were dropped about a year ago. The the source was dropped recently. Targets Ruby 2.2 so I'm going to open an issue asking about targeting 2.3 and folding this work into trunk.
$ git clone https://github.com/rubyomr-preview/ruby.git --branch ruby_2_2_omr --recursive
$ cd ruby
$ autoconf
$ ./configure SPEC=linux_x86-64 --with-omr-jit
$ make
$ make install
This should build it for you to try out. I urge others to try it out and give feedback.We haven't turned on the issue tracker on that repo, but the glue has it's issues turned on: https://github.com/rubyomr-preview/rbjitglue
Working on smoothing this out a bit, and also currently working on upgrading the JIT to target 2.4
Ruby+OMR is developed in three pieces: 1) the language independent core, then 2) The language dependent portions (i.e., the VM, and what we call the 'glue')
The language independent core is here: https://github.com/eclipse/omr. It's under active development, :)
I'm actually working on Ruby 2.4 support right now (supremely not prime time, but if you're curious, the branch is here: https://github.com/mgaudet/ruby/tree/ruby_2_4_omr_preliminar...)
Additionally we don't even boot on JRuby today, I wish we did but it is hard to justify effort here.
Just by playing around with RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR you can reduce memory consumption quite a bit.
https://github.com/discourse/discourse_docker/blob/master/te...
RUBY_GC_HEAP_GROWTH_MAX_SLOTS is critical without it you get way too much bloat. The default is very problematic I discussed this with ko1 but not sure if we can get a better default in for 2.4.
what is the blocking issue there?
For people starting out that is not so much a problem, but if you have moderate to large traffic you need a lot of machines just for the RAM. This is coupled with the fact that many Ruby libraries eschew streaming of files and connections and put things straight into memory despite Ruby having excellent block constructs that easily allow chunking/streaming. So apps also spike in memory and they need a large overhead as well.
I am currently tasked with maintaining and upgrading a Spree 2.2 (ancient version) and Rails 4.0.8 (very old) based e-commerce shop and even slight modernizations are extremely painful and have proven impossible. The test suite I inherired is sorely lacking in several areas so I can't even rely on that. Most of the problems in the project are related to payments and real cart / order / payment flows and the payment processors don't provide sandboxing environment. Cute, right?
I tried once to upgrade it to the latest Rails 4.1.* and I drowned a weekend in fuzzy and frustrating tasks I can't even remember anymore, only to give up frustrated after what has practically been almost 3 work days fit in a weekend.
My point is, if you want your app not to die by itself (while adding some features and chasing 2-year old bugs that cost your customer money even today) and if you want to modernize it, you _will_ suffer. Not a matter of "if", it's a matter of "how and when".
One of my former customers used to say this about rewrites or maintenance: "Both approaches are gonna hurt a lot so we might as well suffer for the right cause". Granted that's not an universal rule but IMO it's quite on point for a good chunk of scenarios.
"Keep rewriting your program. Rewriting a program often yields a cleaner design. But it would have advantages even if it didn't: you have to understand a program completely to rewrite it, so there is no better way to get one loaded into your head."
Why does everything have to be black and white anyway.
I am well aware that Elixir is different than Ruby, even though it borrows some great syntax ideas!
People always seem to compare extremely simple microservices to Rails apps and exclaim how much faster it is in comparison.
https://github.com/mroth/phoenix-showdown/blob/master/README...