New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Move object allocation out of loop #17077
Move object allocation out of loop #17077
Conversation
a749220
to
a27d463
Compare
They probably should be 🍦.freeze |
Right now BenchmarkCleaner allocates hundreds of strings on every request with an exception. This patch moves those strings to be generated at boot once and re-used. ## Bench Methods I took a modified form of CodeTriage https://github.com/schneems/codetriage-ko1-test-app/blob/master/perf.rake and ran given rake tasks with and without the patch to BacktraceCleaner. I made an endpoint results in exception ``` def index raise “foo" end ``` The gem `memory_profiler` was used to capture objects allocated for a single request. Then `benchmark/ips` was used to test the speed of the patch. You will see that we use less objects and the code becomes measurably faster with this patch. ## With patch: Memory for one request: ``` Total allocated 7441 Total retained 37 ``` Requests per second: ``` Calculating ------------------------------------- ips 4 i/100ms ------------------------------------------------- ips 43.0 (±4.7%) i/s - 216 in 5.037733s ``` ## Without patch: Memory used for one request: ``` Total allocated 11599 Total retained 35 ``` Requests per second: ``` Calculating ------------------------------------- ips 3 i/100ms ------------------------------------------------- ips 39.4 (±7.6%) i/s - 198 in 5.052783s ``` ## Analysis The patch is faster: ``` (43.0 - 39.4 ) / 39.4 * 100 # => 9 # % ~ speed bump ``` It also allocates less objects: ``` 11599 - 7441 # => 4158 ``` These strings are allocated on __EVERY SINGLE REQUEST__. This patch saves us 4158 objects __PER REQUEST__ with exception. Faster errors == Faster applications
a27d463
to
dfbcfaf
Compare
👍 noticed this too |
If those blocks are hit often enough to matter, perhaps we should look to avoid the |
Not entirely sure why but the sub seems to have much less impact than the completely new string allocation Here's the sub require 'benchmark/ips'
str1 = "foo"
str2 = "foo"
regex = /f/
empty = ''
Benchmark.ips do |x|
x.report("sub") { str1.dup.sub(regex, empty) }
x.report("sub!") { str2.dup.sub!(regex, empty) }
end The results show the in place sub being faster, but not by much
Comparing creating a string to using an existing string: Benchmark.ips do |x|
x.report("new string") { '' }
x.report("previously allocated string") { empty }
end It is much slower
I think it would be unexpected for a cleaner to modify the strings in place, though maybe it's fine. I'm not too familiar with all the use cases here. If someone else wants it and thinks its worth it it, let me know. I think changing |
…ocations Decrease backtrace cleaner object allocations
Right now BenchmarkCleaner allocates hundreds of strings on every request with an exception. This patch moves those strings to be generated at boot once and re-used.
Bench Methods
I took a modified form of CodeTriage https://github.com/schneems/codetriage-ko1-test-app/blob/master/perf.rake and ran given rake tasks with and without the patch to BacktraceCleaner.
I made an endpoint results in exception
The gem
memory_profiler
was used to capture objects allocated for a single request. Thenbenchmark/ips
was used to test the speed of the patch.You will see that we use less objects and the code becomes measurably faster with this patch.
With patch:
Memory for one request:
Requests per second:
Without patch:
Memory used for one request:
Requests per second:
Analysis
The patch is faster:
It also allocates less objects:
These strings are allocated on EVERY SINGLE REQUEST. This patch saves us 4158 objects PER REQUEST with exception.
Faster errors == Faster applications