今天我给 Ruby China 做了一个改进,然后每个页面的请求耗时少了 3.5ms
大概是这样的,据之前的统计发现,javascript_include_tag
, stylesheet_link_tag
这两个 helper 在 production 环境下面依然会浪费 3ms 左右的时间,3ms 啊,Cache 命中的时候,Ruby China 的列表实际上才 60ms 左右(顺便说一下,去年是 80ms,见这篇帖子)。
并且按说不应该的,Assets Pipeline 都已经编译好了,跑起来以后就不需要计算了啊(至少那个 URL 是不会变的)
于是看了一下 ActionView 里面这个 helper 的实现,发现好复杂的逻辑,并且在那些逻辑前面没用 Cache 的迹象...
actionview/lib/action_view/helpers/asset_tag_helper.rb
def javascript_include_tag(*sources)
options = sources.extract_options!.stringify_keys
path_options = options.extract!('protocol', 'extname').symbolize_keys
sources.uniq.map { |source|
tag_options = {
"src" => path_to_javascript(source, path_options)
}.merge!(options)
content_tag(:script, "", tag_options)
}.join("\n").html_safe
end
于是动了一下(当然不能一概而论,Ruby China 适合我后面的做法)
module ApplicationHelper
$html_cache = {}
def fetch_cache_html(*keys)
cache_key = keys.join("/")
if controller.perform_caching && (html = $html_cache[cache_key])
return html
else
html = yield
# logger.info "HTML CACHE Missed: #{cache_key}"
$html_cache[cache_key] = html
end
html
end
def stylesheet_link_tag_with_cached(name)
fetch_cache_html("stylesheets_link_tag",name) do
stylesheet_link_tag(name, 'data-turbolinks-track' => true)
end
end
def javascript_include_tag_with_cached(name)
fetch_cache_html("javascript_include_tag", name) do
javascript_include_tag(name, 'data-turbolinks-track' => true)
end
end
def cached_asset_path(name)
fetch_cache_html("asset_path", name) do
asset_path(name)
end
end
end
用 stylesheet_link_tag_with_cached
替代原来的,减少复杂的计算,效果有了~
$html_cache
将会在 Rails 进程重启的时候丢掉,在 Assets 这几个 helper 并且参数也简单只有一个文件的情况下,正好可以用进程内存存放 Helper 的 HTML 结果,反复利用。
典型的空间换时间案例
最终我发现,慢是在 sprockets-rails 这边,它覆盖了 javascript_include_tag
和 stylesheet_link_tag
实现了它的一些机制。
已经根据此文的实现,给 sprockets-rails 提交了 PR 不知道 Rails core team 怎么看