bootsnap 1.4.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.github/CODEOWNERS +2 -0
  3. data/.github/probots.yml +2 -0
  4. data/.gitignore +17 -0
  5. data/.rubocop.yml +20 -0
  6. data/.travis.yml +21 -0
  7. data/CHANGELOG.md +122 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/CONTRIBUTING.md +21 -0
  10. data/Gemfile +9 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.jp.md +231 -0
  13. data/README.md +304 -0
  14. data/Rakefile +13 -0
  15. data/bin/ci +10 -0
  16. data/bin/console +15 -0
  17. data/bin/setup +8 -0
  18. data/bin/test-minimal-support +7 -0
  19. data/bin/testunit +8 -0
  20. data/bootsnap.gemspec +46 -0
  21. data/dev.yml +10 -0
  22. data/ext/bootsnap/bootsnap.c +829 -0
  23. data/ext/bootsnap/bootsnap.h +6 -0
  24. data/ext/bootsnap/extconf.rb +19 -0
  25. data/lib/bootsnap.rb +48 -0
  26. data/lib/bootsnap/bundler.rb +15 -0
  27. data/lib/bootsnap/compile_cache.rb +43 -0
  28. data/lib/bootsnap/compile_cache/iseq.rb +73 -0
  29. data/lib/bootsnap/compile_cache/yaml.rb +63 -0
  30. data/lib/bootsnap/explicit_require.rb +50 -0
  31. data/lib/bootsnap/load_path_cache.rb +78 -0
  32. data/lib/bootsnap/load_path_cache/cache.rb +208 -0
  33. data/lib/bootsnap/load_path_cache/change_observer.rb +63 -0
  34. data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +107 -0
  35. data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +93 -0
  36. data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +18 -0
  37. data/lib/bootsnap/load_path_cache/loaded_features_index.rb +148 -0
  38. data/lib/bootsnap/load_path_cache/path.rb +114 -0
  39. data/lib/bootsnap/load_path_cache/path_scanner.rb +50 -0
  40. data/lib/bootsnap/load_path_cache/realpath_cache.rb +32 -0
  41. data/lib/bootsnap/load_path_cache/store.rb +90 -0
  42. data/lib/bootsnap/setup.rb +39 -0
  43. data/lib/bootsnap/version.rb +4 -0
  44. data/shipit.rubygems.yml +0 -0
  45. metadata +174 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2d4f38db9a609c2adb0a0ede991bd993dff7ae59885cb1722eb699658211fd96
4
+ data.tar.gz: 9f363c21a154e123693f18e48073451c6cfe6c05ec378c980e6ef770f01e658c
5
+ SHA512:
6
+ metadata.gz: 925f595e21911c61ff7cf3a86cb055d25e56bb65a8a6437c513f25bfea3aec6c086259808b5aed3289e5d82f66706f98314f31d2ff3886d85edeb47085d6a918
7
+ data.tar.gz: 31507ba8393d47361f8332064a9a39220392a4242e1f3f3c3a88c4de032b51eb8aab8d3769869fec7f9da55400ce773c42aecc52604d5293c9ff7ea3f9f40e54
@@ -0,0 +1,2 @@
1
+ # mvm:maintainer
2
+ * @burke
@@ -0,0 +1,2 @@
1
+ enabled:
2
+ - cla
@@ -0,0 +1,17 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ *.gem
15
+ *.db
16
+ mkmf.log
17
+ .rubocop-*
@@ -0,0 +1,20 @@
1
+ inherit_from:
2
+ - http://shopify.github.io/ruby-style-guide/rubocop.yml
3
+
4
+ AllCops:
5
+ Exclude:
6
+ - 'vendor/**/*'
7
+ - 'tmp/**/*'
8
+ TargetRubyVersion: '2.3'
9
+
10
+ # This doesn't take into account retrying from an exception
11
+ Lint/HandleExceptions:
12
+ Enabled: false
13
+
14
+ # allow String.new to create mutable strings
15
+ Style/EmptyLiteral:
16
+ Enabled: false
17
+
18
+ # allow the use of globals which makes sense in a CLI app like this
19
+ Style/GlobalVars:
20
+ Enabled: false
@@ -0,0 +1,21 @@
1
+ language: ruby
2
+ sudo: false
3
+
4
+ os:
5
+ - linux
6
+ - osx
7
+
8
+ rvm:
9
+ - ruby-2.4
10
+ - ruby-2.5
11
+ - ruby-head
12
+
13
+ matrix:
14
+ allow_failures:
15
+ - rvm: ruby-head
16
+ include:
17
+ - rvm: jruby
18
+ os: linux
19
+ env: MINIMAL_SUPPORT=1
20
+
21
+ script: bin/ci
@@ -0,0 +1,122 @@
1
+ # 1.4.5
2
+
3
+ * MRI 2.7 support
4
+ * Fixed concurrency bugs
5
+
6
+ # 1.4.4
7
+
8
+ * Disable ISeq cache in `bootsnap/setup` by default in Ruby 2.5
9
+
10
+ # 1.4.3
11
+
12
+ * Fix some cache permissions and umask issues after switch to mkstemp
13
+
14
+ # 1.4.2
15
+
16
+ * Fix bug when removing features loaded by relative path from `$LOADED_FEATURES`
17
+ * Fix bug with propagation of `NameError` up from nested calls to `require`
18
+
19
+ # 1.4.1
20
+
21
+ * Don't register change observers to frozen objects.
22
+
23
+ # 1.4.0
24
+
25
+ * When running in development mode, always fall back to a full path scan on LoadError, making
26
+ bootsnap more able to detect newly-created files. (#230)
27
+ * Respect `$LOADED_FEATURES.delete` in order to support code reloading, for integration with
28
+ Zeitwerk. (#230)
29
+ * Minor performance improvement: flow-control exceptions no longer generate backtraces.
30
+ * Better support for requiring from environments where some features are not supported (especially
31
+ JRuby). (#226)k
32
+ * More robust handling of OS errors when creating files. (#225)
33
+
34
+ # 1.3.2
35
+
36
+ * Fix Spring + Bootsnap incompatibility when there are files with similar names.
37
+ * Fix `YAML.load_file` monkey patch to keep accepting File objects as arguments.
38
+ * Fix the API for `ActiveSupport::Dependencies#autoloadable_module?`.
39
+ * Some performance improvements.
40
+
41
+ # 1.3.1
42
+
43
+ * Change load path scanning to more correctly follow symlinks.
44
+
45
+ # 1.3.0
46
+
47
+ * Handle cases where load path entries are symlinked (https://github.com/Shopify/bootsnap/pull/136)
48
+
49
+ # 1.2.1
50
+
51
+ * Fix method visibility of `Kernel#require`.
52
+
53
+ # 1.2.0
54
+
55
+ * Add `LoadedFeaturesIndex` to preserve fix a common bug related to `LOAD_PATH` modifications after
56
+ loading bootsnap.
57
+
58
+ # 1.1.8
59
+
60
+ * Don't cache YAML documents with `!ruby/object`
61
+ * Fix cache write mode on Windows
62
+
63
+ # 1.1.7
64
+
65
+ * Create cache entries as 0775/0664 instead of 0755/0644
66
+ * Better handling around cache updates in highly-parallel workloads
67
+
68
+ # 1.1.6
69
+
70
+ * Assortment of minor bugfixes
71
+
72
+ # 1.1.5
73
+
74
+ * bugfix re-release of 1.1.4
75
+
76
+ # 1.1.4 (yanked)
77
+
78
+ * Avoid loading a constant twice by checking if it is already defined
79
+
80
+ # 1.1.3
81
+
82
+ * Properly resolve symlinked path entries
83
+
84
+ # 1.1.2
85
+
86
+ * Minor fix: deprecation warning
87
+
88
+ # 1.1.1
89
+
90
+ * Fix crash in `Native.compile_option_crc32=` on 32-bit platforms.
91
+
92
+ # 1.1.0
93
+
94
+ * Add `bootsnap/setup`
95
+ * Support jruby (without compile caching features)
96
+ * Better deoptimization when Coverage is enabled
97
+ * Consider `Bundler.bundle_path` to be stable
98
+
99
+ # 1.0.0
100
+
101
+ * (none)
102
+
103
+ # 0.3.2
104
+
105
+ * Minor performance savings around checking validity of cache in the presence of relative paths.
106
+ * When coverage is enabled, skips optimization instead of exploding.
107
+
108
+ # 0.3.1
109
+
110
+ * Don't whitelist paths under `RbConfig::CONFIG['prefix']` as stable; instead use `['libdir']` (#41).
111
+ * Catch `EOFError` when reading load-path-cache and regenerate cache.
112
+ * Support relative paths in load-path-cache.
113
+
114
+ # 0.3.0
115
+
116
+ * Migrate CompileCache from xattr as a cache backend to a cache directory
117
+ * Adds support for Linux and FreeBSD
118
+
119
+ # 0.2.15
120
+
121
+ * Support more versions of ActiveSupport (`depend_on`'s signature varies; don't reiterate it)
122
+ * Fix bug in handling autoloaded modules that raise NoMethodError
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at burke@libbey.me. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
@@ -0,0 +1,21 @@
1
+ # Contributing to Bootsnap
2
+
3
+ We love receiving pull requests!
4
+
5
+ ## Standards
6
+
7
+ * PR should explain what the feature does, and why the change exists.
8
+ * PR should include any carrier specific documentation explaining how it works.
9
+ * Code _must_ be tested, including both unit and remote tests where applicable.
10
+ * Be consistent. Write clean code that follows [Ruby community standards](https://github.com/bbatsov/ruby-style-guide).
11
+ * Code should be generic and reusable.
12
+
13
+ If you're stuck, ask questions!
14
+
15
+ ## How to contribute
16
+
17
+ 1. Fork it ( https://github.com/Shopify/bootsnap/fork )
18
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
19
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
20
+ 4. Push to the branch (`git push origin my-new-feature`)
21
+ 5. Create a new Pull Request
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ source 'https://rubygems.org'
3
+
4
+ # Specify your gem's dependencies in bootsnap.gemspec
5
+ gemspec
6
+
7
+ group :development do
8
+ gem 'rubocop'
9
+ end
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Shopify, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,231 @@
1
+ # Bootsnap [![Build Status](https://travis-ci.org/Shopify/bootsnap.svg?branch=master)](https://travis-ci.org/Shopify/bootsnap)
2
+
3
+ Bootsnap は RubyVM におけるバイトコード生成やファイルルックアップ等の時間のかかる処理を最適化するためのライブラリです。ActiveSupport や YAML もサポートしています。[内部動作](#内部動作)もご覧ください。
4
+
5
+ 注意書き: このライブラリは英語話者によって管理されています。この README は日本語ですが、日本語でのサポートはしておらず、リクエストにお答えすることもできません。バイリンガルの方がサポートをサポートしてくださる場合はお知らせください!:)
6
+
7
+ ### パフォーマンス
8
+
9
+ * [Discourse](https://github.com/discourse/discourse) では、約6秒から3秒まで、約50%の起動時間短縮が確認されています。
10
+ * 小さなアプリケーションでも、50%の改善(3.6秒から1.8秒)が確認されています。
11
+ * 非常に巨大でモノリシックなアプリである Shopify のプラットフォームでは、約25秒から6.5秒へと約75%短縮されました。
12
+
13
+ ## 使用方法
14
+
15
+ この gem は macOS と Linux で作動します。まずは、`bootsnap` を `Gemfile` に追加します:
16
+
17
+ ```ruby
18
+ gem 'bootsnap', require: false
19
+ ```
20
+
21
+ Rails を使用している場合は、以下のコードを、`config/boot.rb` 内にある `require 'bundler/setup'` の直後に追加してください。
22
+
23
+ ```ruby
24
+ require 'bootsnap/setup'
25
+ ```
26
+
27
+ 単に `gem 'bootsnap', require: 'bootsnap/setup'` と指定することも技術的には可能ですが、最大限のパフォーマンス改善を得るためには Bootsnap をできるだけ早く読み込むことが重要です。
28
+
29
+ この require の仕組みは[こちら](https://github.com/Shopify/bootsnap/blob/master/lib/bootsnap/setup.rb)で確認できます。
30
+
31
+ Rails を使用していない場合、または、より多くの設定を変更したい場合は、以下のコードを `require 'bundler/setup'` の直後に追加してください(早く読み込まれるほど、より多くのものを最適化することができます)。
32
+
33
+ ```ruby
34
+ require 'bootsnap'
35
+ env = ENV['RAILS_ENV'] || "development"
36
+ Bootsnap.setup(
37
+  cache_dir:           'tmp/cache',         # キャッシュファイルを保存する path
38
+  development_mode:     env == 'development', # 現在の作業環境、例えば RACK_ENV, RAILS_ENV など。
39
+ load_path_cache: true, # キャッシュで LOAD_PATH を最適化する。
40
+  autoload_paths_cache: true,                 # キャッシュで ActiveSupport による autoload を行う。
41
+  disable_trace:       true,                 # (アルファ) `RubyVM::InstructionSequence.compile_option = { trace_instruction: false }`をセットする。
42
+  compile_cache_iseq:   true,                 # ISeq キャッシュをコンパイルする
43
+  compile_cache_yaml:   true                 # YAML キャッシュをコンパイルする
44
+ )
45
+ ```
46
+
47
+ **ヒント**: `require 'bootsnap'` を `BootLib::Require.from_gem('bootsnap', 'bootsnap')` で、 [こちらのトリック](https://github.com/Shopify/bootsnap/wiki/Bootlib::Require)を使って置き換えることができます。こうすると、巨大な`$LOAD_PATH`がある場合でも、起動時間を最短化するのに役立ちます。
48
+
49
+ 注意: Bootsnap と [Spring](https://github.com/rails/spring) は別領域の問題を扱うツールです。Bootsnap は個々のソースファイルの読み込みを高速化します。一方で、Spring は起動されたRailsプロセスのコピーを保持して次回の起動時に起動プロセスの一部を完全にスキップします。2つのツールはうまく連携しており、どちらも新しく生成された Rails アプリケーションにデフォルトで含まれています。
50
+
51
+ ### 環境
52
+ Bootsnapのすべての機能はセットアップ時の設定に従って開発、テスト、プロダクション、および他のすべての環境で有効化されます。Shopify では、この gem を問題なくすべての環境で安全に使用しています。
53
+
54
+ 特定の環境で機能を無効にする場合は、必要に応じて適切な ENV 変数または設定を考慮して設定を変更することをおすすめします。
55
+
56
+ ## 内部動作
57
+
58
+ Bootsnap は、処理に時間のかかるメソッドの結果をキャッシュすることで最適化しています。これは、大きく分けて2つのカテゴリに分けられます。
59
+
60
+ * [Path Pre-Scanning](#path-pre-scanning)
61
+ * `Kernel#require` と `Kernel#load` を `$LOAD_PATH` フルスキャンを行わないように変更します。
62
+ * `ActiveSupport::Dependencies.{autoloadable_module?,load_missing_constant,depend_on}` を `ActiveSupport::Dependencies.autoload_paths` のフルスキャンを行わないようにオーバーライドします。
63
+ * [Compilation caching](#compilation-caching)
64
+ * Ruby バイトコードのコンパイル結果をキャッシュするためのメソッド `RubyVM::InstructionSequence.load_iseq` が実装されています。
65
+ * `YAML.load_file` を YAML オブジェクトのロード結果を MessagePack でキャッシュするように変更します。 MessagePack でサポートされていないタイプが使われている場合は Marshal が使われます。
66
+
67
+ ### Path Pre-Scanning
68
+
69
+ _(このライブラリは [bootscale](https://github.com/byroot/bootscale) という別のライブラリを元に開発されました)_
70
+
71
+ Bootsnap の初期化時、あるいはパス(例えば、`$LOAD_PATH`)の変更時に、`Bootsnap::LoadPathCache` がキャッシュから必要なエントリーのリストを読み込みます。または、必要に応じてフルスキャンを実行し結果をキャッシュします。
72
+ その後、たとえば `require 'foo'` を評価する場合, Ruby は `$LOAD_PATH` `['x', 'y', ...]` のすべてのエントリーを繰り返し評価することで `x/foo.rb`, `y/foo.rb` などを探索します。これに対して Bootsnap は、キャッシュされた require 可能なファイルと `$LOAD_PATH` を見ることで、Rubyが最終的に選択するであろうパスで置き換えます。
73
+
74
+ この動作によって生成された syscall を見ると、最終的な結果は以前なら次のようになります。
75
+
76
+ ```
77
+ open x/foo.rb # (fail)
78
+ # (imagine this with 500 $LOAD_PATH entries instead of two)
79
+ open y/foo.rb # (success)
80
+ close y/foo.rb
81
+ open y/foo.rb
82
+ ...
83
+ ```
84
+
85
+ これが、次のようになります:
86
+
87
+ ```
88
+ open y/foo.rb
89
+ ...
90
+ ```
91
+
92
+ `autoload_paths_cache` オプションが `Bootsnap.setup` に与えられている場合、`ActiveSupport::Dependencies.autoload_paths` をトラバースする方法にはまったく同じ最適化が使用されます。
93
+
94
+ `*_path_cache` を機能させるオーバーライドを図にすると、次のようになります。
95
+
96
+ ![Bootsnapの説明図](https://cloud.githubusercontent.com/assets/3074765/24532120/eed94e64-158b-11e7-9137-438d759b2ac8.png)
97
+
98
+ Bootsnap は、 `$LOAD_PATH` エントリを安定エントリと不安定エントリの2つのカテゴリに分類します。不安定エントリはアプリケーションが起動するたびにスキャンされ、そのキャッシュは30秒間だけ有効になります。安定エントリーに期限切れはありません。コンテンツがスキャンされると、決して変更されないものとみなされます。
99
+
100
+ 安定していると考えられる唯一のディレクトリは、Rubyのインストールプレフィックス (`RbConfig::CONFIG['prefix']`, または `/usr/local/ruby` や `~/.rubies/x.y.z`)下にあるものと、`Gem.path` (たとえば `~/.gem/ruby/x.y.z`) や `Bundler.bundle_path` 下にあるものです。他のすべては不安定エントリと分類されます。
101
+
102
+ [`Bootsnap::LoadPathCache::Cache`](https://github.com/Shopify/bootsnap/blob/master/lib/bootsnap/load_path_cache/cache.rb) に加えて次の図では、エントリの解決がどのように機能するかを理解するのに役立つかもしれません。経路探索は以下のようになります。
103
+
104
+ ![パス探索の仕組み](https://cloud.githubusercontent.com/assets/3074765/25388270/670b5652-299b-11e7-87fb-975647f68981.png)
105
+
106
+ また、`LoadError` のスキャンがどれほど重いかに注意を払うことも大切です。もし Ruby が `require 'something'` を評価し、そのファイルが `$LOAD_PATH` にない場合は、それを知るために `2 * $LOAD_PATH.length` のファイルシステムアスセスが必要になります。Bootsnap は、ファイルシステムにまったく触れずに `LoadError` を投げ、この結果をキャッシュします。
107
+
108
+ ## Compilation Caching
109
+
110
+ *(このコンセプトのより分かりやすい解説は [yomikomu](https://github.com/ko1/yomikomu) をお読み下さい。)*
111
+
112
+ Ruby には複雑な文法が実装されており、構文解析は簡単なオペレーションではありません。1.9以降、Ruby は Ruby ソースを内部のバイトコードに変換した後、Ruby VM によって実行してきました。2.3.0 以降、[RubyはAPIを公開し](https://ruby-doc.org/core-2.3.0/RubyVM/InstructionSequence.html)、そのバイトコードをキャッシュすることができるようになりました。これにより、同じファイルが複数ロードされた時の、比較的時間のかかる部分をバイパスすることができます。
113
+
114
+ また、アプリケーションの起動時に YAML ドキュメントの読み込みに多くの時間を費やしていることを発見しました。そして、 MessagePack と Marshal は deserialization にあたって YAML よりもはるかに高速であるということに気付きました。そこで、YAML ドキュメントを、Ruby バイトコードと同じコンパイルキャッシングの最適化を施すことで、高速化しています。Ruby の "バイトコード" フォーマットに相当するものは MessagePack ドキュメント (あるいは、MessagePack をサポートしていないタイプの YAML ドキュメントの場合は、Marshal stream)になります。
115
+
116
+ これらのコンパイル結果は、入力ファイル(FNV1a-64)のフルパスのハッシュを取って生成されたファイル名で、キャッシュディレクトリに保存されます。
117
+
118
+ Bootsnap 無しでは、ファイルを `require` するために生成された syscall の順序は次のようになっていました:
119
+
120
+ ```
121
+ open /c/foo.rb -> m
122
+ fstat64 m
123
+ close m
124
+ open /c/foo.rb -> o
125
+ fstat64 o
126
+ fstat64 o
127
+ read o
128
+ read o
129
+ ...
130
+ close o
131
+ ```
132
+
133
+ しかし Bootsnap では、次のようになります:
134
+
135
+ ```
136
+ open /c/foo.rb -> n
137
+ fstat64 n
138
+ close n
139
+ open /c/foo.rb -> n
140
+ fstat64 n
141
+ open (cache) -> m
142
+ read m
143
+ read m
144
+ close m
145
+ close n
146
+ ```
147
+
148
+ これは一見劣化していると思われるかもしれませんが、性能に大きな違いがあります。
149
+
150
+ *(両方のリストの最初の3つの syscalls -- `open`, `fstat64`, `close` -- は本質的に有用ではありません。[このRubyパッチ](https://bugs.ruby-lang.org/issues/13378)は、Boosnap と組み合わせることによって、それらを最適化しています)*
151
+
152
+ Bootsnap は、64バイトのヘッダーとそれに続くキャッシュの内容を含んだキャッシュファイルを書き込みます。ヘッダーは、次のいくつかのフィールドで構成されるキャッシュキーです。
153
+
154
+ - `version`、Bootsnapにハードコードされる基本的なスキーマのバージョン
155
+ - `ruby_platform`、`RUBY_PLATFORM`(x86_64-linux-gnuなど)変数とglibcバージョン(Linuxの場合)またはOSバージョン(BSD、macOSの場合は` uname -v`)のハッシュ
156
+ - `compile_option`、`RubyVM::InstructionSequence.compile_option` の返り値
157
+ - `ruby_revision`、コンパイルされたRubyのバージョン
158
+ - `size`、ソースファイルのサイズ
159
+ - `mtime`、コンパイル時のソースファイルの最終変更タイムスタンプ
160
+ - `data_size`、バッファに読み込む必要のあるヘッダーに続くバイト数。
161
+
162
+ キーが有効な場合、キャッシュがファイルからロードされます。そうでない場合、キャッシュは再生成され、現在のキャッシュを破棄します。
163
+
164
+ # 最終的なキャッシュ結果
165
+
166
+ 次のファイル構造があるとします。
167
+
168
+ ```
169
+ /
170
+ ├── a
171
+ ├── b
172
+ └── c
173
+ └── foo.rb
174
+ ```
175
+
176
+ そして、このような `$LOAD_PATH` があるとします。
177
+
178
+ ```
179
+ ["/a", "/b", "/c"]
180
+ ```
181
+
182
+ Bootsnap なしで `require 'foo'` を呼び出すと、Ruby は次の順序で syscalls を生成します:
183
+
184
+ ```
185
+ open /a/foo.rb -> -1
186
+ open /b/foo.rb -> -1
187
+ open /c/foo.rb -> n
188
+ close n
189
+ open /c/foo.rb -> m
190
+ fstat64 m
191
+ close m
192
+ open /c/foo.rb -> o
193
+ fstat64 o
194
+ fstat64 o
195
+ read o
196
+ read o
197
+ ...
198
+ close o
199
+ ```
200
+
201
+ しかし Bootsnap では、次のようになります:
202
+
203
+ ```
204
+ open /c/foo.rb -> n
205
+ fstat64 n
206
+ close n
207
+ open /c/foo.rb -> n
208
+ fstat64 n
209
+ open (cache) -> m
210
+ read m
211
+ read m
212
+ close m
213
+ close n
214
+ ```
215
+
216
+ Bootsnap なしで `require 'nope'` を呼び出すと、次のようになります:
217
+
218
+ ```
219
+ open /a/nope.rb -> -1
220
+ open /b/nope.rb -> -1
221
+ open /c/nope.rb -> -1
222
+ open /a/nope.bundle -> -1
223
+ open /b/nope.bundle -> -1
224
+ open /c/nope.bundle -> -1
225
+ ```
226
+
227
+ ...そして、Bootsnap で `require 'nope'` を呼び出すと、次のようになります...
228
+
229
+ ```
230
+ # (nothing!)
231
+ ```