Rails 3 Performance - Not Good Enough
blog.tstmedia.com
blog.tstmedia.com
But there are people who have convinced themselves that as long as it scales out, it doesn't matter how many servers you have to run. And if the reason is that something that's known statically is checked billions of times at runtime they don't care.
For a framework, the concept of premature optimization makes no sense.
What if the new thing had 100x more (useful) features, should it still be as fast?
Performance is a trade-off and sometimes it's worth compromising.
Even if you're saying that only in performance there can be no compromises (but generally people who say things like no compromises don't bound those statements) it will cause large compromises in other areas, such a future feature development or long term maintainability and readability.
I don't feel your original comment reflected a position on knowing where to (or not) compromise, you made a strong assertion to take one option off the table in all situations.
In my experience, developers who are make blanket rules up front about what can and can't be changed in the future development of a system don't end up making much of value.
That's just my perspective, and I apologise if I've misread your character.
I totally share your sentiment about having to be frugal because of wasteful frameworks. My approach is to prefer libraries over frameworks. I start out using the most bare-bones configuration possible (no frameworks, no ORM, no CMS, etc) and then I selectively add well maintained libraries created by people whose attitude I understand.
I think code reuse is generally overrated, particularly when it comes to the rather trivial things that web frameworks do.
This is probably not very helpful to you. I apologize.
The interpreter is the sticky part here and something for which Perl gets some criticism in this arena, but the Perl interpreter is also written in C and is quite fast, if not small.
Bag on Perl all you like as a language, I know I do (and I use it, as well as Rails), but if you can get past the language hump mod_perl will knock you sideways. You can easily pump out a network card's worth of static content with a leftover desktop machine, and DBI is no slouch when it comes to integrating dynamic content.
Slashdot, Valueclick, Craigslist, IMDB...all mod_perl.
"I would never ever release a new version of anything that is significantly slower than the previous one. It's simply a bug in my view."
I was trying to get across that we give some up performance for convenience now and again and it's fine to do so. I worked on the BBC iPlayer API which was written on mod_perl, by the way.
https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/...
What was originally a performance optimization on 1.9.2 caused a terrible slowdown on 1.8.7. It was reported and fixed within two days.
Does that still fit into your "not giving a shit about performance" critique? I think the "real problem" is that there are two major releases of MRI Ruby (not to mention JRuby) that have very different performance characteristics.
So, creating a situation where you're guaranteeing an exception is raised during non-exceptional execution flow is generally a bad idea. It may be cleaner or more concise code-wise, but performance-wise it almost certainly will always be slower.
For the most part I believe the parent is correct in that there is a much stronger emphasis placed on code conciseness/clarity than there is on avoiding performance anti-patterns. It's hard to measure whether the gains in "code agility" outweigh the local performance hit by making it easier to surface other performance problems. When there's less code involved it usually follows that it's easier to spot and fix problems. But I worry because my own experience suggests that death of a thousand cuts is a considerably harder situation to get out of than dealing with a handful of gnarlier bottlenecks.
Except that exception handling is so fast in 1.9.2 that it made this change a performance optimization in 1.9.2. Look lower in this topic for benchmarks showing that Rails 3 is faster in 1.9.2 than rails2 is under 1.8.7.
>performance-wise it almost certainly will always be slower
Except, it wasn't slower in the Ruby that most people running Rails 3 should be running (1.9.2).
I also point out, again, that they immediately fixed this problem when someone pointed out how slow it was in 1.8.7.
Now, I could be completely off-base and 1.9.2 may be the only system out there that has managed to make exception handling a cheap operation. But I'm highly skeptical of that. I think the more likely scenario is exception handling is much cheaper in 1.9.2 than 1.8.7, but still not cheap enough to favor over virtually anything else. I've not read anywhere in the linked issue or in the following comments that suggests using responds_to? was any slower than handling a NoMethodError exception. If it is, that largely points at a failure of responds_to? more than it trumpets the speed of ruby exception handling. And pretending that exception handling is fast enough for flow control and thus other problems needn't be fixed can land a community in serious trouble.
Now obviously there are exceptions to every rule. But the default position of any programmer really should be to reserve exceptions for exceptional circumstances because they are almost axiomatically slow, regardless of language or platform.
Its easy enough to spout platitudes like "don't use exceptions for flow control", except remember two basic facts about the commit that I linked:
1. It was using exceptions for flow control as an optimization 2. That optimization was indeed faster on 1.9.2
On top of that, when the rails team was notified about how bad this "optimization" was in 1.8.7, they immediately fixed it.
I just don't see how any of this fits into "don't give a shit about performance".
I think it is very interesting that the specific test produced in the original post is actually faster under Rails 3 and Ruby 1.9.2 than it is under Rails 2 and 1.8.7.
My only grief with you, fauigerzigerk, is that you claim the Rails team doesn't "give a shit about performance", when the performance problems seem to be related to people using an older version of Ruby. I think you are making an extraordinary claim -- that the rails team doesn't "give a shit", in in fact, they clear do give a shit.
http://stackoverflow.com/questions/4789248/rails-3-initializ...
http://stackoverflow.com/questions/5516428/slow-deployment-f...
In Ruby 1.9.2 (p180 and -head), the application starts 26 seconds. In REE 1.8.7, it takes 8 seconds.
With 1.8.7 REE's garbage collection settings tuned, the page renders in two seconds.
With 1.9.2, the page renders in four seconds.
http://dl.dropbox.com/u/6607107/dev.tanga.gif
46% of the time is spent garbage collecting.
The problem is that performance is an academically interesting problem to us geeks. We love to optimize, make quicker and oh-so clever.
Also, if you look at what just a few selective type hints in clojure can do to performance or how terse Scala code is, you have to come to the conclusion that performance and productivity can go hand in hand.
Yes. On top of that the ruby ecosystem also seems to attract a certain class of programmers who simply don't know how their magic ruby code translates to CPU, I/O and memory operations.
My pet example is a certain popular rails auth framework that hits the database for every single request, to look up tokens that could simply be baked into the cookie. But there are plenty more - reviewing rails gems for performance is generally an exercise in frustration.
It's a pity. Moore's Law mostly mitigates the low performance of MRI and we could be fine that way. But it can not mitigate a culture of incompetent design.
As for "incompetent design" - I don't know what you are on about. I think that Rails 3 is a frigging triumph of elegant, modular design. There are very many gems in the ruby ecosystem that I believe any unbiased person would say demonstrate exemplary design. In fact, I would say that the average rubyist is much more concerned with good design than, say, your average java dev. The whole reason I switched to ruby was because of the beauty of the language. And what is beauty but good design applied?
Of course there are always exceptions but on the whole I think this comment is quite unfair.
No, I'm explicitly criticizing Ruby. I've spent years in Python-land and the average code quality (and btw documentation standard) is much higher over there. Which is not to say Python doesn't have its own problems, but less so in this particular area.
say that the average rubyist is much more concerned with good design than, say, your average java dev.
Good design is a relative term. In ruby I frequently see it interpreted as: Pack as many layers of magic as possible, and then test that with as many layers of testing-frameworks as possible.
That's not too different from the common architecture-astronouting in java. One could argue it's just a different flavor. In either ecosystem the implementation of the actual business concerns ("What does this code actually do?") too often feels like an afterthought.
Disclaimer: I'm not condemning either language. I like Ruby and use it a lot, but this is one aspect that I can't ignore.
I have the same feeling sometimes. I wish the Ruby community would not be so concerned with magic and aesthetics, and target clarity.
Aesthetics and clarity are most certainly different in other ways (think pretty sites vs simple clear sites) but that's another discussion.
Isn't the entire goal of computer science to abstract away complexity?
I'm pretty sure you could design a perfectly good car by treating an engine as a black box with a clearly defined set of inputs an outputs without caring how those inputs are translated in the outputs. Which is the goal of a framework such as rails.
I'm sure you could build a car that looked pretty, but not one that ran well.
It makes a great deal of sense to understand the complexity you're abstracting away, first. Otherwise, you never really know what you're doing. At the end of the day, you're executing on a computer and you're doing yourself a serious disservice if you're ignoring that fact and hoping either Moore's Law or horizontal scaling are the answer.
I'm happy to be proven wrong, but I've written a lot of apps before I moved to Rails, and we used to avoid re-authing the user / looking it up on each request, and it's just never made a bit of difference to us.
We're a postgres shop and the problem we've run into lately is just the sheer number of open connections to the DB. Using something like PgBouncer helps tremendously, but it still is contingent on there being idle connections. Certainly read slaves could be thrown at the problem, too. But it's mounting complexity for something that isn't all that necessary shrug
https://github.com/rails/rails/issues/398 https://github.com/rails/rails/issues/485
What they and everyone else experiencing performance problems should do, is to find one action that has the biggest performance difference between running under Rails 2 and Rails 3 and then profile it - easiest way is by wrapping the action in an around filter and using RubyProf. If there is a obvious performance problem somewhere, you will most often see a method that has a really huge number of calls and %self% time in comparison to all the others. Then you have to figure out what in detail happens - I ended up doing this by putting "begin; raise StandardException.new; rescue => e; puts e.backtrace; end;" into the "hot" methods to get call stacks and then went on to read the code involved.
user system total real
rails3-1.9.2 2.240000 0.200000 2.440000 ( 3.127072)
rails2-ree 2.530000 0.290000 2.820000 ( 3.578471)
rails2-1.8.7 2.920000 0.210000 3.130000 ( 3.876215)
rails3-ree 3.140000 0.250000 3.390000 ( 4.111465)
rails3-1.8.7 3.560000 0.220000 3.780000 ( 4.505166)
Of course, the author was looking into a specific problem — this doesn't mean that Rails 3 on MRI 1.9.2 is faster than Rails 3 on REE.Run on an iMac9,1 with a 2.66 GHz Core 2 Duo. uname -a:
Darwin Roy.local 10.7.0 Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386 i386
I disabled spotlight, but who knows what other processes might have spawned in the middle of the benchmark. Your mileage may vary.
In 1.9.2 (p180 and head), my rails 3 application starts in 26 seconds.
With REE 1.8.7, it starts in 8 seconds or so.
Also, just to be clear, I'm not the author of the linked post or the benchmark.
I think it boils down to generality. A database is a very specific thing, and the typical usage of the database (e.g. SELECT * FROM products WHERE id=?) can be very highly optimized.
A programming language or framework is much more general, and it is much harder to optimize common cases without losing layers of abstraction.
For example, it would be much faster to return records as arrays instead of ActiveRecord objects. That would probably be much better for certain uses — say, processing of lots and lots of records. It would be worse for most cases, though, and would make Rails that much harder to use.
This is probably exacerbated by the tendency to do everything in the application instead of in the DB (e.g. not using hand built SQL to get aggregate information). That said, it seems like there's been some movement toward shifting some processing back into the DB (e.g. joins).
I don't think I've ever managed to write a PHP page that took more than 100ms on average for the PHP alone on a decent server, and it's not from a lack of abuse.
Completed 200 OK in 205ms (Views: 168.9ms | ActiveRecord: 23.2ms | Sphinx: 9.6ms)
edit: a couple of notes. This is running on a cheap vps and would be faster on a real server. It would be faster if I used ruby 1.9 as well. If you want to see how fast a bare ruby webserver is then check out this. http://torquebox.org/news/2011/02/23/benchmarking-torquebox/
Rails view/partial rendering is pretty slow in general.
I think the some people could have read the comment I replied to and believe that 400ms is somehow normal. It's not.
That's not to say there aren't caching options for ActiveRecord or Rails. You just have to go out of your way to employ them. There's no out-of-the-box write-through cache of the DB.
* - I became a committer for the Cayenne project but haven't been active over the past couple years.
If you manage to avoid that, you've beaten the slowest parts. The view engine is also slow but I tend to do rendering in the browser these days so that barely matters too me.
If you are careful, it is very realistic to have response overhead from rails hover around 1-2ms for simple things and maybe up to 10-20ms for the most complex business logic (expensive validations). It's kind of odd that people who care about performance assume they have to take the whole package as is.
Generally speaking, I think AR's problem isn't really an API one, it's a great interface. It's that it's implementation is far too clever and would benefit quite a bit from narrowing supported driver styles and removal of some old APIs that really hurt more than help. There could also be better control over opt-in on some behaviors that start adding up in overhead (i.e. change tracking keeps a lot of garbage around).
For our application that meant bringing 1 of 3 application servers down and slowing things down in general. So we're stuck with 3.0.5.
From what I understand the Rails community has declared 1.8.x as a thing of the past (tbf it IS a thing of the past. But in a non-perfect world migrations our not always easy). This may be a good thing (it forced us to march towards 1.9.2) but I'm sure it has caused a lot of problems.
https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/...
https://gist.github.com/919428
https://github.com/rails/rails/commit/a3639be4ed5e89b59c4ad0...
The commit mostly fixes that particular issue (and is included in 3.0.7), but as the article point out Rails 3 AR performance still needs quite a bit of work.
By this time, I wonder what the rails developers should spend their time with: making the framework better for people running the current version of the language or fixing performance problems due to shortcomings of the old version?
If you can't upgrade to 1.9 and the performance of rails 3 isn't good enough for you to upgrade rails, why don't you just stay with 2.3 and upgrade once you are ready to move to ruby 1.9?
That way your unwillingness to upgrade ruby doesn't "waste" the time of the rails developers by causing them to fix issues that don't even apply to more and more people over time.
Note: i'm talking about regressions that only affect 1.8
I have a pretty decent-sized application running on REE and Rails 2.3.x. I've made my code fully 1.9.2 compatible. My specs take on average 3.5 times longer to run in 1.9.2. Profiling that shows that close to 60% of the time is spent in Kernel#require. Now, it's pretty common for people to say only profile in the production environment, but I need to be able to run my specs. And it's not acceptable to shell that out to a CI server or to run REE in dev. mode and deploy with 1.9.2. Running in spork is a gross approximation, too; I've seen way too many issues there.
My suspicion is that if you have a small enough project, the speed difference is negligible. If you have a large project, it becomes much more pronounced. So, it'd really be nice for me to continue to upgrade and improve my Rails-based app without having to drag along an environment that's going to hobble me.
FWIW, I do know people that have started on 1.9.2 and rolled back to REE because speed was such a problem.
But: the solution to this problem isn't not upgrading and then demanding support for your outdated environment. The solution is to fix whatever is slow and try to get a patch upstream.
Now I don't know how likely the ruby developers are to accept a patch, but once this becomes too painful for me, I will try and have a look. I might not be able to fix it, but at least I know that I'm not stuck in the past, terrified and unable to move
I don't really understand why the release of 1.9.2 meant all else had to be dropped. Most other communities continue to support their stable releases. It's not as if 1.9.2 has even displaced 1.8.7 with virtually any of the linux distros either. If your policy is to use security-audited / supported packages, as is the case in many environments, moving to 1.9.2 is a dealbreaker.
Anyway, supporting 1.8.7 and 1.9.2 is trivial in most cases. I'm not demanding support for my environment, which I prefer to think of more as stable and battle-tested than "outdated." But I don't understand actively dropping support for it either.
:( Rails 3 is awesome, except for the performance issues. I'm not sure how to go about looking into it. When I profile, the bulk of the time is spent garbage collecting apparently.
Here is the result:
(aka a 1.8.6 branch)
There are hacks and fixes to this like spork, but none of this should be necessary. The performance issues are not debated in the open enough. I don't understand how those startup times are acceptable to anyone, but I rarely get responses when I ask questions about it.
http://stackoverflow.com/questions/5738443/why-is-rails-boot...
UPDATE: I have the latest top of the line Macbook Air - 2.13 Ghz Core 2 Duo with SSD.
I have other issues with Rails, but this is the only potential deal breaker. (I tend toward functional clarity instead of "human" readability in the magic debate, while most "Rubyists" would rather pollute the namespace (e.g. metawhere) and create 100 line method_missing calls to make one command slightly prettier aesthetically. And Cucumber - ugh what a ridiculous contraption that is.)
It's a huge productivity killer for me. And as other comments have stated, it's surprising this issue is not discussed more.
Keep console open and use reload! when you change your models, for starting a server I don't have much other than keep it running the background, running a test -- use spork, migrate your db and run rake tasks -- try to take advantage of the fact that you can specify multiple rake tasks at a time; "rake db:migrate db:test:prepare"
Django, by comparison, bootstraps the environment almost instantly.
I have also been looking for an answer for how to deal with this issue, so I just started a bounty on your question for 200 reputation. Looking forward to a good answer; it may very well convince to go back to Rails.
The Rails community (at least at the time) was so much larger and more open that it seemed like a better bet. Rails 3 was coming up and I knew Yehuda was doing brilliant things with the Rails 3.0 architecture. Plus, Moore's law right? - not helping!
I'd love for some hard core Rails guys to get in here and offer some real solutions! As much as I have issues with some things about Rails, it is largely a great tool for me, and I know it so well I'd like to be able to continue using it without tearing my hair out every time I need to run a simple test.
If you want a blissfully magic-free language try Go ( http://golang.org ).
Beetle looks promising. http://xing.github.com/beetle/ You can do both RPC and async messaging. (Might want to wait a few days before using it, there's issues with some libraries that it depends on that will be fixed soon).
Besides, why should I have to set up and learn RabbitMQ to have a decently performing web framework? This is 2011 and this shit is slower than my old Java framework running on an ancient processor in 1995.
But I share your frustrations re: "metawhare" :) Solution is just to hang around with responsible devs.
Long startup times or not, sounds like you should probably be using autotest or watchr. 20-30 seconds is not normal. Unless the app has a very large amount of code and dependencies, that sounds like an environment issue.
"while most "Rubyists" would rather pollute the namespace"
That's unfair, and I say that as someone who is very adamant about clarity, explicitness and simplicity in my apps. In my experience, most (not all, but most) of the libraries where people are doing weird things are libraries that are far from necessary, so avoiding that is trivial.
For instance, you cite metawhere. First, I haven't investigated the implementation in depth, but, FYI, it doesn't appear to use method_missing at all. Secondly, I'd be very hesitant to include a library like this in an app, regardless of the implementation, since it's too invasive of a dependency.
I don't think the performance problems are affecting ALL applications - some people don't see any problems. I do think there is something wrong in Ruby or Rails that doesn't happen in all code paths.
It's a good suggestion, but doesn't solve the problem.
If you use autotest or watchr you aren't starting up the environment very frequently.
Autotest helps some, but 20-30 seconds is pretty standard fare. Maybe I use too many gems, but I don't think I should forgo reusing community code to reduce start times.
Maybe it's unfair to make a generalization about "rubyists," but it seems people in the community get overly excited about something that saves 5 characters in their codebase or makes something readable to non-devs (for all those non-devs who will read your code.
Another example - the ubiquity of DelayedJob. The standard usage is to insert delay into your call chain, so foo.delay.send_emails instead of foo.send_emails. This pollutes the ENTIRE object space with the method delay, in addition to the fact that it doesn't work well on many objects. I complained about this practice and the response was that it was clearly better to write foo.delay.send_emails instead of Delayed.add_job(foo, :send_emails) or something. For me, I find any pollution of namespace where conflict is possible highly questionable, although sometimes it's ok.
Metawhere adds methods like .eq and .lt to Symbol (symbol is something like an intern string, e.g. :foo or 'foo in Python).
Using too many gems is an problem for a lot of reasons, though. You are introducing dependencies that you then need to manage and update, they often do more than you need to while being harder to maintain (you might end up with a bunch of forks in your gemfile as things get outdated), they are sometimes invasive and will require big rewrites to switch from.
"it seems people in the community get overly excited about something that saves 5 characters"
I agree (though I'd modify to to say "some people in the community"), I just find that I don't encounter it much when I'm following best practices myself.
You get the point.
I use gems that save me tons of time. If not for gems, I would not be using Rails.
Also, since you asked :-) I would question #5. Auto admin libraries (and this applies to multiple languages and frameworks) are generally heavy, too general and have code hidden away in the library. In contrast, banging out crud views and controllers in a locked-down namespace is easy, only relies on same dependencies as the rest of the app, can be customized for the specific business needs and all the code is in one obvious place.
I suppose your logic makes sense if you're gainfully employed, but I bang this stuff out for myself and clients. A 90% solution with one line of code sure beats weeks of work.
We use datamapper on our app, and were looking at doing some of our analytics on mongo at one point, but since both mongomapper and datamapper both define 'helper' methods on Symbols, they can't both be in use at the same time.
If we _really_ wanted to use mongo it would need to be from a completely separate codebase either talking to the db via Sequel or something, or talk to our main app via REST calls - neither being a particularly pleasant solution.
http://qwerly.com runs a tight and fast Node.js stack at the front-end with a test suite that takes less than a second to run (a few hundred assertions). I'll dedicate much time to the framework around it's tests and make sure it's never slow.
I'm jealous. That would probably save me a huge portion of my development time. Node is my top contender if I dump rails.
http://www.stifflog.com/2011/05/15/why-is-rails-startup-so-s...
I'm not that deeply in Ruby/RubyGems/Bundler internals to be 100% sure, but on the other hand I really can't see how it could work reasonably fast while using the strategy for looking up libraries it uses.
If I calculate (20 seconds) x (number of times I bootstrap rails), it might even be worth my time to have a go at it.
There is a bug supposed to be fixed in ruby 1.9.3 that addresses something to do with the load path and bootstrap performance, but no idea when that's due to land. If it's truly the cause, it seems like this should be a patch to 1.9.2 instead.
You might also want to see if this is related:
https://github.com/rdp/faster_require/blob/master/lib/faster...
https://img.skitch.com/20110515-x4r8414pkei9j3h73d1sy67iud.j...
1.9.2 and 1.9.3 is really slow at starting up bundler/rails/gems.
Please don't call something with 221 dependencies a blank Rails project. More the amount of conflicting half baked gems you've added makes it an unfair complaint about Rails. I've assure you that there is no language / framework in the universe in which does not take a hit when adding too many dependencies. Either execution wise but more often it just breaks your spirit.
Anyway back to numbers, on 3 year old desktop under Linux, executing tests on the "blank" project yielded:
<pre> real 0m8.698s user 0m5.996s sys 0m2.532s </pre>
For some reason ruby is much slower on OSX than linux, that is an interesting project to investigate. I've think the platform difference shows the 10 second difference, spite the slower CPU. It would be quite interesting to understand why ruby is slow on OSX. Each to his own itch to scratch.
There are 28 gems specified in the Gemfile. That's not an unreasonable amount of gems. With dependencies, the total amount of gems being required is 76.
Hell, only using Rails and sqlite-ruby will require 26 gems to be installed.
Other than a fairly standard list of gems in the Gemfile and one Model with two columns, the application is empty.
On my machine with Ruby 1.9.2-p180, it takes 20 seconds to start. And the application is empty!
I'm on a 2.66 GHZ i5 imac, not an SSD.
For me on 1.8.7, it takes about 7-8 seconds. Annoying because the application is empty, it's just 28 gems being loaded. On 1.9.2, it's 20 seconds or so.
Have you tried spork? https://github.com/timcharper/spork
Also, have you opened a ticket or looked to see if there is one already?
Took a little time to work it out as I am new to PostgreSQL but it was worth it to avoid a pause in the problem solving cycle.
Problem and solution explained on my blog: http://www.roryhart.net/code/slow-create-database-with-postg...
I'm not trolling; I'm genuinely curious about this.