Ruby's Pathname module is notoriously slow, and Sprockets makes pretty heavy use of it, which imposes a pretty hefty performance penalty when doing asset resolution and compilation.
I've been tinkering with a Pathname monkeypatch in an attempt to improve Sprockets' performance. I realize that this is more of a stdlib concern than it is a Sprockets concern, but Sprockets could realize some immediate performance gains by reducing or eliminating Pathname usage.
I'm testing this via Guard with body = Rails.application.assets.find_asset(file).body - nothing all that complex.
I'm comparing this against a more direct Sass invocation, via:
context = ::Rails.application.assets.context_class.new(::Rails.application.assets.context_class, file, file)
options = @config.merge(custom: { resolver: ::Sass::Rails::Resolver.new(context) })
body = ::Sass::Engine.for_file(file, options).render
Pathname usage on the Sprockets codepath (unpatched):

Pathname usage on the Sprockets codepath (patched):

And Pathname usage on the more direct codepath:

The patch I'm working with is pretty straightforward - I'm just patching #join to not do the whole set of Pathname gymnatics. It still passes the Ruby pathname test suite, too! (Though it does take a shortcut by dropping Windows support; that's easily fixed by checking for File::ALT_SEPARATOR as well)
require 'pathname'
class Pathname
def relative?
@path[0] != File::SEPARATOR
end
def join(*args)
last = args.last
if last.to_s[0] == File::SEPARATOR
if last.is_a? Pathname
last
else
Pathname.new last
end
else
Pathname.new(File.join @path, *args.map(&:to_s))
end
end
end
And some quick numbers:
# Standard sprockets
20:07:12 - INFO - [1/1] (2.09 sec) wufoo/default.css.sass -> wufoo/default.css
20:07:16 - INFO - [1/1] (2.04 sec) wufoo/default.css.sass -> wufoo/default.css
20:07:20 - INFO - [1/1] (2.06 sec) wufoo/default.css.sass -> wufoo/default.css
20:15:13 - INFO - [5/5] (6.55 sec) app.css.sass -> app.css
20:15:38 - INFO - [5/5] (5.64 sec) app.css.sass -> app.css
20:15:57 - INFO - [5/5] (6.00 sec) app.css.sass -> app.css
# With Pathname patches
20:06:26 - INFO - [1/1] (1.53 sec) wufoo/default.css.sass -> wufoo/default.css
20:06:30 - INFO - [1/1] (1.50 sec) wufoo/default.css.sass -> wufoo/default.css
20:06:33 - INFO - [1/1] (1.53 sec) wufoo/default.css.sass -> wufoo/default.css
20:17:25 - INFO - [5/5] (5.03 sec) app.css.sass -> app.css
20:17:37 - INFO - [5/5] (5.03 sec) app.css.sass -> app.css
20:17:58 - INFO - [5/5] (5.12 sec) app.css.sass -> app.css
It should be pretty obvious that leaning on Pathname so hard is causing some pretty measurable performance degradation. It may be worth moving to File methods or even just custom high-performance helpers if at all possible.
Ruby's Pathname module is notoriously slow, and Sprockets makes pretty heavy use of it, which imposes a pretty hefty performance penalty when doing asset resolution and compilation.
I've been tinkering with a Pathname monkeypatch in an attempt to improve Sprockets' performance. I realize that this is more of a stdlib concern than it is a Sprockets concern, but Sprockets could realize some immediate performance gains by reducing or eliminating Pathname usage.
I'm testing this via Guard with
body = Rails.application.assets.find_asset(file).body- nothing all that complex.I'm comparing this against a more direct Sass invocation, via:
Pathname usage on the Sprockets codepath (unpatched):
Pathname usage on the Sprockets codepath (patched):
And Pathname usage on the more direct codepath:
The patch I'm working with is pretty straightforward - I'm just patching
#jointo not do the whole set of Pathname gymnatics. It still passes the Ruby pathname test suite, too! (Though it does take a shortcut by dropping Windows support; that's easily fixed by checking forFile::ALT_SEPARATORas well)And some quick numbers:
It should be pretty obvious that leaning on Pathname so hard is causing some pretty measurable performance degradation. It may be worth moving to File methods or even just custom high-performance helpers if at all possible.