1 个不稳定版本
使用旧的 Rust 2015
0.0.2 | 2017 年 3 月 3 日 |
---|
#596 在 文件系统
5.5MB
153K SLoC
FasterPath
这个库为我的 Rails 应用页面加载时间节省了超过 30%。
本项目的 主要目标 是提高 Ruby 最常用区域的性能,因为路径关系和文件查找目前是性能的巨大瓶颈。因此,路径性能更新可能不仅限于更改 Pathname 类,还将涉及相关方法和类的更改。
用户可以选择直接为此库编写应用程序,或者选择改进或修补现有的标准库。改进是缩小范围,修补将是重型锤子 ;-)
原因
我阅读了一篇关于新的 Sprockets 3.0 系列比 2.0 系列更快的博客文章,所以我试了一下。它没有更快,反而使我的网站加载时间增加了 31.8%。所以我回到了 2.0 系列,并在 Rails 上检查了被调用最多的方法和应用程序大部分时间花费在哪里。结果大约 80% (据我所知) 的时间和调用都是文件路径处理。这是令人震惊的,但处理资产时会更糟。 这就是为什么我们需要以最有效的方式处理这些负载密集的方法!
这里是一个 Rails 堆栈分析片段,其中包含一些最常用且耗时的方法。
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
1466 (20.4%) 305 (4.2%) Pathname#plus
1628 (22.7%) 162 (2.3%) Pathname#+
234 (3.3%) 117 (1.6%) ActionView::PathResolver#find_template_paths
2454 (34.2%) 62 (0.9%) Pathname#join
57 (0.8%) 52 (0.7%) ActiveSupport::FileUpdateChecker#watched
760 (10.6%) 47 (0.7%) Pathname#relative?
131 (1.8%) 25 (0.3%) ActiveSupport::FileUpdateChecker#max_mtime
88 (1.2%) 21 (0.3%) Sprockets::Asset#dependency_fresh?
18 (0.3%) 18 (0.3%) ActionView::Helpers::AssetUrlHelper#compute_asset_extname
108 (1.5%) 14 (0.2%) ActionView::Helpers::AssetUrlHelper#asset_path
以下是更多统计数据。从 Rails 加载到我的主页,这些方法被调用了这么多次。而且主页内容很少。
Pathname#to_s called 29172 times.
Pathname#<=> called 24963 times.
Pathname#chop_basename called 24456 times
Pathname#initialize called 23103 times.
File#initialize called 23102 times.
Pathname#absolute? called 4840 times.
Pathname#+ called 4606 times.
Pathname#plus called 4606 times.
Pathname#join called 4600 times.
Pathname#extname called 4291 times.
Pathname#hash called 4207 times.
Pathname#to_path called 2706 times.
Pathname#directory? called 2396 times.
Pathname#entries called 966 times.
Dir#each called 966 times.
Pathname#basename called 424 times.
Pathname#prepend_prefix called 392 times.
Pathname#cleanpath called 392 times.
Pathname#cleanpath_aggressive called 392 times.
Pathname#split called 161 times.
Pathname#open called 153 times.
Pathname#exist? called 152 times.
Pathname#sub called 142 times.
在进一步挖掘后,我发现 Pathname 在 Sprockets 2 中使用得很频繁,但在 Sprockets 3 中,它们切换到调用 Ruby 的更快方法,即 File#initialize
和 Dir#each
。看起来他们自己用 Ruby 实现了所有的路径处理。通过切换到更原始的代码方法,他们实现了一些性能提升,但随后在构建在之上的许多方法调用中损失了更多性能。
如果您想在这个库中在 Rails 中看到最佳结果,您可能需要使用 Sprockets 2.0 系列。否则,这个库需要重写 Sprockets 本身。
我以前也提到过关于Sprockets的事情,但这还需要更新两个其他gem。这些是我升级的gem和版本,我将其视为第一组(Sprockets 2)和第二组(Sprockets 3)。我的数据基于方法调用,而不是源代码。
Sprockets 2 组 | Sprockets 3 组 |
---|---|
sprockets 2.12.4 | sprockets 3.6 |
sass 3.2.19 | sass 5.0.4 |
bootstrap-sass 3.3.4.1 | bootstrap-sass 3.3.6 |
状态
- Rust编译正在运行
- 方法最可能是稳定的
- 欢迎测试人员和开发者加入!
安装
确保已安装Rust
curl -sSf https://static.rust-lang.org/rustup.sh | sh
将以下行添加到您的应用程序的Gemfile中
gem 'faster_path', '~> 0.1.0'
然后执行
$ bundle
或者您可以自行安装
$ gem install faster_path
MAC用户:目前Mac用户需要手动安装扩展。转到gem目录并运行 cargo build --release
。为此问题已提交问题,我在寻找有Mac的人来帮忙。
视觉基准测试
现在在Faster Path中的基准测试可以产生性能改进的视觉图表。当你运行 rake bench
时,图表艺术将放置在 doc/graph/
。以下是 chop_basename
方法的性能改进结果。
用法
当前实现的方法
FasterPath Rust 实现 | Ruby 2.3.1 实现 | 性能改进 |
---|---|---|
FasterPath.绝对? |
路径名#绝对? |
93.9% |
FasterPath.chop_basename |
路径名#chop_basename |
50.6% |
FasterPath.相对? |
路径名#相对? |
93.2% |
FasterPath.空白? |
||
FasterPath.目录? |
路径名#目录? |
25.5% |
FasterPath.add_trailing_separator |
路径名#add_trailing_separator |
46.8% |
FasterPath.has_trailing_separator? |
路径名#has_trailing_separator |
61.2% |
您可以选择直接使用方法,或者使用范围更改来重写标准库中的行为,或者甚至调用一个方法来对每个地方进行猴子补丁。
注意: Ruby STDLIB 中的 Pathname#chop_basename
有一个与空白字符串相关的bug,这是与FasterPath实现行为差异的唯一之处。
对于范围 精炼,您需要
require "faster_path/optional/refinements"
using FasterPath::RefinePathname
而对于猴子补丁的锤子,您可以这样做
require "faster_path/optional/monkeypatches"
FasterPath.sledgehammer_everything!
不稳定的选择性部分
可能存在回归的选项方法是不稳定的。 这些将 不会 默认包含在猴子补丁中。使用 FasterPath::RefineFile
精炼时要小心。要尝试它们,请使用环境标志 WITH_REGRESSION
。这些方法是为了改进而存在的。
FasterPath 实现 | Ruby 实现 |
---|---|
FasterPath.dirname |
File.dirname |
FasterPath.basename |
File.basename |
FasterPath.extname |
File.extname |
我观察到(以及一些其他人)的观察结果是,Rust对C代码的 File
的实现具有相似的结果,但性能似乎取决于CPU缓存,可能取决于64位/32位系统环境。
FasterPath 开发者:其中大部分需要重写,请在测试中使用 WITH_REGRESSION=true TEST_MONKEYPATCHES=true
。您可以在TravisCI的“允许失败”下看到当前的结果。
开始开发
主要的针对方法大多列在上面的为什么部分。您可能会发现Pathname的Ruby源代码很有用,请参阅Ruby源、C源、测试,以及查看文档。
方法将尽可能用Rust编写。即使只是用Rust方法像!absolute?
(非绝对)来写一个不是Ruby的,也会降低已经获得的39%的性能。只要可能,就用Rust实现。
检出仓库后,请确保您已安装Rust,然后运行bundle
。运行rake test
来运行测试,以及运行rake bench
进行基准测试。
在维基百科上学习和分享性能提示!
构建和运行测试
首先,通过运行bundle
捆绑宝石的开发依赖项。
然后,构建rust代码
rake build_src
FasterPath使用Ruby Spec套件进行测试,以确保它与本地实现兼容,并且还有自己的测试套件测试其猴子补丁和改进功能。
要一次性运行所有测试,只需运行rake
。要运行所有Ruby spec测试,请运行mspec
。
要运行FasterPath自己的套件中的单个测试或基准
# An individual test file:
ruby -I lib:test test/benches/absolute_benchmark.rb
# All tests:
rake minitest
要运行单个Ruby spec测试,请使用相对于spec/ruby_spec
的路径运行mspec
,例如
# A path to a file or a directory:
mspec core/file/basename_spec.rb
# Tests most relevant to FasterPath:
mspec core/file library/pathname
# All tests:
mspec
贡献
在GitHub上欢迎提交错误报告和拉取请求:https://github.com/danielpclark/faster_path。
许可
MIT许可证或APACHE 2.0,任选其一。