Improve performance and memory usage for options_for_select with Ruby 1.8 #2302
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
It would seem that
FormOptionsHelper#options_for_select
uses some syntactic sugar (Symbol#to_proc
) which can cause severe performance problems under certain circumstances, when usingruby 1.8.7
w/Rails 3-1-stable
In my case, I was rendering a page with 10 selects of about 700 options each (there is a use case for that, trust me), and after the first couple of requests it would take as long as 8 seconds more, per request (from 300ms to ~9secs). Eeek!
After a long session with
perftools.rb
andrack-perftools_profiler
I managed to track it down tooptions_for_select
and its use ofmap(&:to_s)
. The symptoms were seemingly random, long, garbage collection pauses, in the middle of a request.Perftools.rb
pointed me toArray.map
creating tens of thousands ofProc
objects as a side-effect of callingSymbol#to_proc
.Depending on the initial free memory allocation, garbage collection would not kick in until after 800MB of memory needed to be inspected by the GC. Unsurprisingly, my machine would stop responding during those long GC pauses.
I have not managed to reproduce with code the long GC pauses, I guess they need to happen inside the Rails rendering pipeline and under a specific memory situation, but I have managed to benchmark a significant improvement when replacing
Symbol#to_proc
with a block:The results:
Now, I know 3.1 is only offering token Ruby 1.8.7 support and that 3.2 will most likely require 1.9.2, but this can be a real performance problem in real-world applications. One not worth not addressing for the sake of a bit of syntactic sugar...
The fix in this pull request should have zero side-effects and would save the mane of many a developer.