sprockets: Performance regression while requiring lots of files

Hi guys,

I’m following rails/sprockets-rails#225 and I would like to share with you a performance regression I’m experiencing after moving from sprocket-rails 2.1.3 to 3.0 or master.

It turns out that the issue is not in sprocket-rails but rather in the underlying sprockets since most of the time is spent resolving asset paths (cc @eileencodes).

Here is what I’m seeing on my personal app (ruby-prof output):

Measure Mode: wall_time
Thread ID: 70253745167340
Fiber ID: 70253756137300
Total: 1.518107
Sort by: self_time

 %self      total      self      wait     child     calls  name
 19.84      0.302     0.301     0.000     0.000     1368   <Module::Marshal>#load_without_autoloading
  5.71      0.087     0.087     0.000     0.000      143   Zlib::Inflate#inflate
  4.60      0.070     0.070     0.000     0.000     1339   <Class::File>#utime
  4.22      0.074     0.064     0.000     0.010     1908   Sprockets::URIUtils#join_file_uri
  3.72      0.448     0.056     0.000     0.392     1339   Sprockets::EncodingUtils#unmarshaled_deflated
  3.58      0.054     0.054     0.000     0.000    21137   String#scan
  2.48      0.222     0.038     0.000     0.184    20937   Sprockets::Resolve#parse_path_extnames
  2.16      0.033     0.033     0.000     0.000     4095   <Class::File>#exist?
  2.03      0.031     0.031     0.000     0.000        2   SQLite3::Statement#step
  1.77      0.055     0.027     0.000     0.029    20939   Array#reverse_each
  1.75      0.048     0.027     0.000     0.021      302   Array#include?
  1.74      0.028     0.026     0.000     0.002     7471  *String#gsub
  1.63      0.025     0.025     0.000     0.000     1370   File#initialize
  1.46      0.022     0.022     0.000     0.000   120894   String#==
  1.42      0.052     0.022     0.000     0.031     1831   Sprockets::DigestUtils#digest
  1.38      0.021     0.021     0.000     0.000    21201   <Class::File>#basename
  1.32      0.020     0.020     0.000     0.000       20   <Class::Dir>#[]
  1.29      0.020     0.020     0.000     0.000     3203   Regexp#===
  1.28      0.161     0.019     0.000     0.141    20937   Sprockets::PathUtils#match_path_extname
screen shot 2015-08-01 at 13 41 11

And here is a small project reproducing the issue: redox/perf_regression_sprockets. In that application a page that took 20ms to be rendered using sprockets-rails 2.1.3 now takes 60ms with the latest master branch.

Any chance it could be related to the “compat” resolving layer? Let me know if I can help!

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Comments: 37 (14 by maintainers)

Commits related to this issue

Most upvoted comments

Alright I did some digging and here’s my derailed stackprof of slow methods

before

Booting: development
Endpoint: "/"
       user     system      total        real
100 requests 26.830000   1.780000  28.610000 ( 28.866952)
Running `stackprof tmp/2016-06-09T00:42:10-04:00-stackprof-cpu-myapp.dump`. Execute `stackprof --help` for more info
==================================
  Mode: cpu(1000)
  Samples: 7184 (0.03% miss rate)
  GC: 1013 (14.10%)
==================================
     TOTAL    (pct)     SAMPLES    (pct)     FRAME
      1894  (26.4%)        1894  (26.4%)     Pathname#chop_basename
      1104  (15.4%)         937  (13.0%)     Hike::Index#entries
      1314  (18.3%)         427   (5.9%)     BetterErrors::ExceptionExtension#set_backtrace
       391   (5.4%)         386   (5.4%)     Sprockets::Mime#mime_types
      1466  (20.4%)         305   (4.2%)     Pathname#plus
      1384  (19.3%)         272   (3.8%)     BindingOfCaller::BindingExtensions#callers
       402   (5.6%)         205   (2.9%)     Hike::Index#find_aliases_for
       373   (5.2%)         175   (2.4%)     Hike::Index#sort_matches
      1628  (22.7%)         162   (2.3%)     Pathname#+
       234   (3.3%)         117   (1.6%)     ActionView::PathResolver#find_template_paths
       126   (1.8%)         101   (1.4%)     Hike::Index#build_pattern_for
       206   (2.9%)          98   (1.4%)     Hike::Index#pattern_for
      1696  (23.6%)          93   (1.3%)     Hike::Index#match
        65   (0.9%)          65   (0.9%)     Hike::Trail#stat
       118   (1.6%)          63   (0.9%)     Hike::Index#initialize
      2454  (34.2%)          62   (0.9%)     Pathname#join
        59   (0.8%)          59   (0.8%)     Time#compare_with_coercion
        57   (0.8%)          52   (0.7%)     ActiveSupport::FileUpdateChecker#watched
      5345  (74.4%)          52   (0.7%)     Sprockets::Rails::Helper#check_errors_for
       760  (10.6%)          47   (0.7%)     Pathname#relative?
        45   (0.6%)          30   (0.4%)     Sprockets::Engines#deep_copy_hash
       131   (1.8%)          25   (0.3%)     ActiveSupport::FileUpdateChecker#max_mtime
        88   (1.2%)          21   (0.3%)     Sprockets::Asset#dependency_fresh?
        39   (0.5%)          18   (0.3%)     ActiveSupport::Inflector#camelize
        18   (0.3%)          18   (0.3%)     String#blank?
        18   (0.3%)          18   (0.3%)     ActionView::Helpers::AssetUrlHelper#compute_asset_extname
        17   (0.2%)          17   (0.2%)     ActiveSupport::SafeBuffer#initialize
        82   (1.1%)          16   (0.2%)     ActiveRecord::Migrator.migrations
       108   (1.5%)          14   (0.2%)     ActionView::Helpers::AssetUrlHelper#asset_path
        14   (0.2%)          14   (0.2%)     ThreadSafe::NonConcurrentCacheBackend#[]

after

Booting: development
Endpoint: "/"
       user     system      total        real
100 requests 34.960000   2.740000  37.700000 ( 37.947141)
Running `stackprof tmp/2016-06-09T00:05:11-04:00-stackprof-cpu-myapp.dump`. Execute `stackprof --help` for more info
==================================
  Mode: cpu(1000)
  Samples: 9465 (0.00% miss rate)
  GC: 1432 (15.13%)
==================================
     TOTAL    (pct)     SAMPLES    (pct)     FRAME
      1261  (13.3%)        1003  (10.6%)     Sprockets::PathUtils#entries
       702   (7.4%)         702   (7.4%)     URI::RFC3986_Parser#split
       611   (6.5%)         611   (6.5%)     Sprockets::PathUtils#path_extnames
       600   (6.3%)         600   (6.3%)     Sprockets::PathUtils#stat
       669   (7.1%)         399   (4.2%)     Sprockets::URITar#initialize
       341   (3.6%)         341   (3.6%)     URI::RFC2396_Parser#escape
      1164  (12.3%)         283   (3.0%)     Sprockets::PathUtils#match_path_extname
       270   (2.9%)         270   (2.9%)     Sprockets::Paths#root
      1419  (15.0%)         235   (2.5%)     Set#initialize
       225   (2.4%)         225   (2.4%)     Set#add
       187   (2.0%)         187   (2.0%)     URI::RFC2396_Parser#unescape
       184   (1.9%)         167   (1.8%)     Sprockets::DigestUtils#digest
       151   (1.6%)         151   (1.6%)     #<Module:0x0000000198d748>.load_with_autoloading
       453   (4.8%)         148   (1.6%)     Set#merge
       950  (10.0%)         147   (1.6%)     Sprockets::URIUtils#split_file_uri
      1163  (12.3%)         140   (1.5%)     Sprockets::Resolve#parse_path_extnames
      7286  (77.0%)         118   (1.2%)     Sprockets::CachedEnvironment#initialize
       116   (1.2%)         116   (1.2%)     Sprockets::PathUtils#split_subpath
       439   (4.6%)         114   (1.2%)     Sprockets::URIUtils#join_file_uri
       218   (2.3%)         109   (1.2%)     ActionView::PathResolver#find_template_paths
       153   (1.6%)         104   (1.1%)     Sprockets::HTTPUtils#find_q_matches
       129   (1.4%)         100   (1.1%)     Sprockets::Mime#extname_map
       188   (2.0%)          98   (1.0%)     Sprockets::URITar#expand
      2024  (21.4%)          95   (1.0%)     Sprockets::Dependencies#resolve_dependency
        89   (0.9%)          89   (0.9%)     Sprockets::PathUtils#absolute_path?
       719   (7.6%)          59   (0.6%)     Sprockets::CachedEnvironment#stat
        68   (0.7%)          57   (0.6%)     ActiveSupport::FileUpdateChecker#watched
        52   (0.5%)          52   (0.5%)     Time#compare_with_coercion
       195   (2.1%)          44   (0.5%)     Sprockets::EncodingUtils#unmarshaled_deflated
        39   (0.4%)          39   (0.4%)     block (2 levels) in <class:Numeric>

So my app is faster with the older sprockets: (from flame graph info of page load)

before

sprockets-rails-2.3.3 (431 samples - 57.09%)
sprockets-2.12.4 (417 samples - 55.23%)

after

sprockets-rails-2.3.3 (610 samples - 63.34%)
sprockets-3.6.0 (601 samples - 62.41%)

It looks like way more sprocket method calls are being done and the app is spending more time in it.