mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
dfbcfafd9a
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
32 lines
1 KiB
Ruby
32 lines
1 KiB
Ruby
require 'active_support/backtrace_cleaner'
|
|
|
|
module Rails
|
|
class BacktraceCleaner < ActiveSupport::BacktraceCleaner
|
|
APP_DIRS_PATTERN = /^\/?(app|config|lib|test)/
|
|
RENDER_TEMPLATE_PATTERN = /:in `_render_template_\w*'/
|
|
EMPTY_STRING = ''.freeze
|
|
SLASH = '/'.freeze
|
|
DOT_SLASH = './'.freeze
|
|
|
|
def initialize
|
|
super
|
|
@root = "#{Rails.root}/".freeze
|
|
add_filter { |line| line.sub(@root, EMPTY_STRING) }
|
|
add_filter { |line| line.sub(RENDER_TEMPLATE_PATTERN, EMPTY_STRING) }
|
|
add_filter { |line| line.sub(DOT_SLASH, SLASH) } # for tests
|
|
|
|
add_gem_filters
|
|
add_silencer { |line| line !~ APP_DIRS_PATTERN }
|
|
end
|
|
|
|
private
|
|
def add_gem_filters
|
|
gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
|
|
return if gems_paths.empty?
|
|
|
|
gems_regexp = %r{(#{gems_paths.join('|')})/gems/([^/]+)-([\w.]+)/(.*)}
|
|
gems_result = '\2 (\3) \4'.freeze
|
|
add_filter { |line| line.sub(gems_regexp, gems_result) }
|
|
end
|
|
end
|
|
end
|