> you really need to throw fast hardware at it for good performance.
That's where — non syntetic benchmark, anecdotal real-world use — Python rips Ruby apart. I cringe every time I wait for "bundle exec {rails console|rails server|rake|cap}". At every point in development, tests and production, ruby execution feels sluggish. Our routine Python dance on my 4 years old Core 2 Duo laptop runs circles around our Ruby stuff on my desktop i7-2400.
2.0 brought Ruby into decent land, but there's so much more to do. Future developments look interesting but I fear about the performance impact of new features (such as refinements).
The big problem with bundle exec (or Ruby startup times in general), is the ludicrous amount filesystem access because of searching through a ridiculous large number of files for each "require". E.g. if you have a ton of gem's installed, most "require" calls will look for the files you require relative to the root of every gem....
Much more extensive use of require_relative, and fewer search paths can fix that entirely.
Try an "strace 2>&1 -e last64,stat64,lstat,stat bundle exec [yourcommand] | less" and be prepared to be shocked at the waste.
(EDIT: This of course assumes you have strace; on most Linux distro's that's just a package install away - I don't know about OS X, and I've got no idea how to make dtrace do the same)
you can also attach to a PID. If you are a developer on OS X it is good to know DTrace and what it is capable of. There are a lot of default scripts installed in OS X and you can write your own (if you have ever run iosnoop or execsnoop then you have already used DTrace scripts), see:
Thanks. I don't actually run much stuff on OS X - I use it as my work desktop, but all my "real work" is done via ssh to Linux boxes, so the OS X box is rarely running more than a browser, iTerm 2 and Thunderbird, all maximized. I only "see" OS X when something crashes....
EDIT: Completely unrelated, I just noticed who you are. We met in Mike's house when I came over for the launch of Edgeio. And I just realized how long ago that is.
It is fairly trivial if you're willing to acknowledge the real problem:
You can't pretend to not know about paths.
You can make the current case faster by optimizing this and that, but it boils down to reducing the number of stat calls, and the ways to do that are:
- Minimize the number of paths in the $LOAD_PATH. Ideally there should be one path there, but that might not be practical.
- require_relative everything when you know where it is.
As a bonus you get substantial less risk of breakage because of accidental filename clashes (yes, I've had filename clashes happen several times).
Now, this isn't quick, because it'd mean making people used to require 'gem-name/file' and have gem/bundle ensure that the default load path contains a symlink to the real current include path for every gem, but this doesn't even need a single interpreter change.
The problem here is not the Ruby implementations, but the gem ecosystem: This only becomes difficult if one wants the interpreter to automagically find files you've dropped all over the filesystem.
The biggest problem here is RubyGems which uses "load the specification for every gem you have installed"-approach. 300 gems installed = 300 files read at startup.
300 files is nothing. I just strace'd an app I'm playing with that does 145,000 stat calls on startup.... It's in the process of being rewritten to do require_relative whenever possible. As for the gems, it'll probably end up with a hack to "precompile" a list of absolute paths after installation...
Another option is to build an index of the various parts of $LOAD_PATH. I monkeypatched (JRuby) to do this at runtime (that is, re-index every launch), and I saw a modest speed up in Rails boot time. If there were a standard way to build a semi-permanent index (updated by 'gem install', etc.), I'd expect the index to give even further gains. Of course, this is more invasive than your suggestions.
Yeah, that's at least in part because of the stat()/lstat()-blowup.
After the first run most of the stat data will be cached in-memory anyway, but tens of thousands of unnecessary system calls hurts even with the actual data in memory...
For our use even assembly does not "rip Ruby apart.", looking at our 2 most common pages, topic list page and topic page, 30-50% of the time is still spent in SQL. Ruby 2.0 bring decent launch times to Rails (important for dev) due to the fixes falcon committed. Overall, the execution engine itself is pretty sweet and we have incredible visibility into the inner workings with flame graphs http://samsaffron.com/archive/2013/03/19/flame-graphs-in-rub... .
There is plenty of optimisation work left to do, but the vast majority of the issues we encounter can be addresses in either the application or libraries.
Sure, Google Go is much faster, no arguing there. Python and PyPy oth would be ballpark similar to Ruby 2.0 these days from benches I have seen.
This. Ruby may be slow compared to compiled languages, but after working on Rails apps where it takes 30 seconds to run the tests, I was delighted to develop a gem using TDD. My gem has 100+ tests, and the suite starts and finishes in less than a second.
Our apps' tests suites are slow mainly because of database access, secondarily because of Rails, with Ruby itself being the smallest factor.
Just as a note: by default any modern rails app is going to call Bundler.require, so you only need to use bundle exec if e.g. your system rake is a different version or your system rails is not 3+. I've heard claims that this can have non-trivial performance effects.
Rails is the biggest offender in terms of startup speed, which I've noticed much more since I started playing with Padrino. But you're right; there's still a lot that could be done to improve speed.
I agree completely with your anecdotal experience. It has been my experience as well.
However, I'm quite sure that the speed difference we've observed here is not due to the language implementations at the interpreter level. The two languages are similar in performance in that respect (Python being maybe incrementally faster, but not enough to notice most of the time). The faster speed noticed in this anecdote has got to be almost entirely due to the superior efficiency of the code in the tools we're using. Bundle is just slow basically.
You're talking about startup time, which is bad in this case because of the overhead Rubygems introduces by adding loads of directories to the require load path. Pre-rubygems, everything lived in a traditional $PREFIX/lib/ruby/{,site-ruby/}$VERSION/ directory just like Perl and Python, and startup times were much more comparable.
Atwood is talking about general runtime performance, and comparing it with the fast, mature JITed VMs that drive CLR languages. Python (and indeed other languages in its class) compare just as badly there.
the big pain at the moment with the MRI GC is that it stops the world, on Discourse we see a 50-60ms stall every few requests. Can totally be mitigated with unicorn and oobgc (we will move to this), still a PITA.
That's where — non syntetic benchmark, anecdotal real-world use — Python rips Ruby apart. I cringe every time I wait for "bundle exec {rails console|rails server|rake|cap}". At every point in development, tests and production, ruby execution feels sluggish. Our routine Python dance on my 4 years old Core 2 Duo laptop runs circles around our Ruby stuff on my desktop i7-2400.
2.0 brought Ruby into decent land, but there's so much more to do. Future developments look interesting but I fear about the performance impact of new features (such as refinements).
I'm definitely keeping an eye on Topaz.