bootsnap 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +5 -0
- data/CHANGELOG.md +9 -0
- data/README.md +75 -55
- data/bootsnap.gemspec +3 -1
- data/dev.yml +3 -0
- data/ext/bootsnap/bootsnap.c +546 -394
- data/ext/bootsnap/bootsnap.h +1 -5
- data/ext/bootsnap/extconf.rb +15 -5
- data/lib/bootsnap/compile_cache/iseq.rb +7 -1
- data/lib/bootsnap/compile_cache/yaml.rb +2 -1
- data/lib/bootsnap/compile_cache.rb +3 -3
- data/lib/bootsnap/load_path_cache/cache.rb +33 -12
- data/lib/bootsnap/load_path_cache/change_observer.rb +4 -4
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +7 -2
- data/lib/bootsnap/load_path_cache/path.rb +13 -3
- data/lib/bootsnap/load_path_cache/path_scanner.rb +1 -0
- data/lib/bootsnap/load_path_cache/store.rb +4 -4
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +1 -0
- metadata +12 -11
- data/ext/bootsnap/crc32.c +0 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3725f53309883dce83894e47eedbb4d8322eabd5
|
|
4
|
+
data.tar.gz: 81e14b1d48fc4431f6ac9bb2729cfb4a69aa0f5c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5a2b45cb55ceffdb5751561b3a35c476ea29bcd3d3a2a68a46521240d35cc1e61cf9ac90265db751b09967655969c7a685725878b5f80a61fd38a0efd7024458
|
|
7
|
+
data.tar.gz: 7f7975023cb1856d5ccada63e8be2e3ab94a751d4b80121821f2b01df6cbb94f5172041020ebb9300b63760da3fc2776e3b9b56160b95b4b0c07f82fbce07860
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# 0.3.0
|
|
2
|
+
|
|
3
|
+
* Migrate CompileCache from xattr as a cache backend to a cache directory
|
|
4
|
+
* Adds support for Linux and FreeBSD
|
|
5
|
+
|
|
6
|
+
# 0.2.15
|
|
7
|
+
|
|
8
|
+
* Support more versions of ActiveSupport (`depend_on`'s signature varies; don't reiterate it)
|
|
9
|
+
* Fix bug in handling autoloaded modules that raise NoMethodError
|
data/README.md
CHANGED
|
@@ -1,8 +1,46 @@
|
|
|
1
|
-
# Bootsnap
|
|
1
|
+
# Bootsnap 
|
|
2
2
|
|
|
3
3
|
**Beta-quality. See [the last section of this README](#trustworthiness).**
|
|
4
4
|
|
|
5
|
-
Bootsnap is a library that plugs into a number of
|
|
5
|
+
Bootsnap is a library that plugs into a number of Ruby and (optionally) `ActiveSupport` and `YAML`
|
|
6
|
+
methods to optimize and cache expensive computations. See [the How Does This Work section](#how-does-this-work) for more information.
|
|
7
|
+
|
|
8
|
+
#### Quick Performance Overview
|
|
9
|
+
- [Discourse](https://github.com/discourse/discourse) reports a boot time reduction of approximately 50%, from roughly 6 to 3 seconds on one machine;
|
|
10
|
+
- One of our smaller internal apps also sees a reduction of 50%, from 3.6 to 1.8 seconds;
|
|
11
|
+
- The core Shopify platform -- a rather large monolithic application -- boots about 75% faster, dropping from around 25s to 6.5s.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
Add `bootsnap` to your `Gemfile`:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem 'bootsnap'
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Next, add this to your boot setup immediately after `require 'bundler/setup'` (i.e. as early as
|
|
22
|
+
possible: the sooner this is loaded, the sooner it can start optimizing things)
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
require 'bootsnap'
|
|
26
|
+
Bootsnap.setup(
|
|
27
|
+
cache_dir: 'tmp/cache', # Path to your cache
|
|
28
|
+
development_mode: ENV['MY_ENV'] == 'development',
|
|
29
|
+
load_path_cache: true, # Should we optimize the LOAD_PATH with a cache?
|
|
30
|
+
autoload_paths_cache: true, # Should we optimize ActiveSupport autoloads with cache?
|
|
31
|
+
disable_trace: false, # Sets `RubyVM::InstructionSequence.compile_option = { trace_instruction: false }`
|
|
32
|
+
compile_cache_iseq: true, # Should compile Ruby code into ISeq cache?
|
|
33
|
+
compile_cache_yaml: true # Should compile YAML into a cache?
|
|
34
|
+
)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Protip:** You can replace `require 'bootsnap'` with `BootLib::Require.from_gem('bootsnap',
|
|
38
|
+
'bootsnap')` using [this trick](https://github.com/Shopify/bootsnap/wiki/Bootlib::Require). This
|
|
39
|
+
will help optimize boot time further if you have an extremely large `$LOAD_PATH`.
|
|
40
|
+
|
|
41
|
+
## How does this work?
|
|
42
|
+
|
|
43
|
+
Bootsnap is a library that plugs into a number of Ruby and (optionally) `ActiveSupport` and `YAML`
|
|
6
44
|
methods. These methods are modified to cache results of expensive computations, and can be grouped
|
|
7
45
|
into two broad categories:
|
|
8
46
|
|
|
@@ -69,7 +107,8 @@ In addition to the [`Bootsnap::LoadPathCache::Cache`
|
|
|
69
107
|
source](https://github.com/Shopify/bootsnap/blob/master/lib/bootsnap/load_path_cache/cache.rb),
|
|
70
108
|
this diagram may help clarify how entry resolution works:
|
|
71
109
|
|
|
72
|
-

|
|
111
|
+
|
|
73
112
|
|
|
74
113
|
It's also important to note how expensive `LoadError`s can be. If ruby invokes
|
|
75
114
|
`require 'something'`, but that file isn't on `$LOAD_PATH`, it takes `2 *
|
|
@@ -78,7 +117,8 @@ result too, raising a `LoadError` without touching the filesystem at all.
|
|
|
78
117
|
|
|
79
118
|
### Compilation Caching
|
|
80
119
|
|
|
81
|
-
*(A
|
|
120
|
+
*(A more readable implementation of this concept can be found in
|
|
121
|
+
[yomikomu](https://github.com/ko1/yomikomu)).*
|
|
82
122
|
|
|
83
123
|
Ruby has complex grammar and parsing it is not a particularly cheap operation. Since 1.9, Ruby has
|
|
84
124
|
translated ruby source to an internal bytecode format, which is then executed by the Ruby VM. Since
|
|
@@ -92,9 +132,8 @@ implementation. We use the same strategy of compilation caching for YAML documen
|
|
|
92
132
|
equivalent of Ruby's "bytecode" format being a MessagePack document (or, in the case of YAML
|
|
93
133
|
documents with types unsupported by MessagePack, a Marshal stream).
|
|
94
134
|
|
|
95
|
-
These compilation results are stored
|
|
96
|
-
the
|
|
97
|
-
like changing mount flags). However, this is a very performant implementation.
|
|
135
|
+
These compilation results are stored in a cache directory, with filenames generated by taking a hash
|
|
136
|
+
of the full expanded path of the input file (FNV1a-64).
|
|
98
137
|
|
|
99
138
|
Whereas before, the sequence of syscalls generated to `require` a file would look like:
|
|
100
139
|
|
|
@@ -116,32 +155,36 @@ With bootsnap, we get:
|
|
|
116
155
|
```
|
|
117
156
|
open /c/foo.rb -> n
|
|
118
157
|
fstat64 n
|
|
119
|
-
|
|
120
|
-
|
|
158
|
+
close n
|
|
159
|
+
open /c/foo.rb -> n
|
|
160
|
+
fstat64 n
|
|
161
|
+
open (cache) -> m
|
|
162
|
+
read m
|
|
163
|
+
read m
|
|
164
|
+
close m
|
|
121
165
|
close n
|
|
122
166
|
```
|
|
123
167
|
|
|
124
|
-
|
|
168
|
+
This may look worse at a glance, but underlies a large performance difference.
|
|
125
169
|
|
|
126
|
-
* `
|
|
127
|
-
|
|
170
|
+
*(The first three syscalls in both listings -- `open`, `fstat64`, `close` -- are not inherently
|
|
171
|
+
useful. [This ruby patch](https://bugs.ruby-lang.org/issues/13378) optimizes them out when coupled
|
|
172
|
+
with bootsnap.)*
|
|
128
173
|
|
|
129
|
-
|
|
174
|
+
Bootsnap writes a cache file containing a 64 byte header followed by the cache contents. The header
|
|
175
|
+
is a cache key including several fields:
|
|
130
176
|
|
|
131
177
|
* `version`, hardcoded in bootsnap. Essentially a schema version;
|
|
178
|
+
* `os_version`, A hash of the current kernel version (on macOS, BSD) or glibc version (on Linux);
|
|
132
179
|
* `compile_option`, which changes with `RubyVM::InstructionSequence.compile_option` does;
|
|
133
|
-
* `
|
|
134
|
-
|
|
135
|
-
* `
|
|
136
|
-
* `
|
|
180
|
+
* `ruby_revision`, the version of Ruby this was compiled with;
|
|
181
|
+
* `size`, the size of the source file;
|
|
182
|
+
* `mtime`, the last-modification timestamp of the source file when it was compiled; and
|
|
183
|
+
* `data_size`, the number of bytes following the header, which we need to read it into a buffer.
|
|
137
184
|
|
|
138
185
|
If the key is valid, the result is loaded from the value. Otherwise, it is regenerated and clobbers
|
|
139
186
|
the current cache.
|
|
140
187
|
|
|
141
|
-
This diagram may help illustrate how it works:
|
|
142
|
-
|
|
143
|
-

|
|
144
|
-
|
|
145
188
|
### Putting it all together
|
|
146
189
|
|
|
147
190
|
Imagine we have this file structure:
|
|
@@ -185,8 +228,13 @@ With bootsnap, we get:
|
|
|
185
228
|
```
|
|
186
229
|
open /c/foo.rb -> n
|
|
187
230
|
fstat64 n
|
|
188
|
-
|
|
189
|
-
|
|
231
|
+
close n
|
|
232
|
+
open /c/foo.rb -> n
|
|
233
|
+
fstat64 n
|
|
234
|
+
open (cache) -> m
|
|
235
|
+
read m
|
|
236
|
+
read m
|
|
237
|
+
close m
|
|
190
238
|
close n
|
|
191
239
|
```
|
|
192
240
|
|
|
@@ -207,40 +255,12 @@ open /c/nope.bundle -> -1
|
|
|
207
255
|
# (nothing!)
|
|
208
256
|
```
|
|
209
257
|
|
|
210
|
-
## Usage
|
|
211
|
-
|
|
212
|
-
Add `bootsnap` to your `Gemfile`:
|
|
213
|
-
|
|
214
|
-
```ruby
|
|
215
|
-
gem 'bootsnap'
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
Next, add this to your boot setup immediately after `require 'bundler/setup'` (i.e. as early as
|
|
219
|
-
possible: the sooner this is loaded, the sooner it can start optimizing things)
|
|
220
|
-
|
|
221
|
-
```ruby
|
|
222
|
-
require 'bootsnap'
|
|
223
|
-
Bootsnap.setup(
|
|
224
|
-
cache_dir: 'tmp/cache', # Path to your cache
|
|
225
|
-
development_mode: ENV['MY_ENV'] == 'development',
|
|
226
|
-
load_path_cache: true, # Should we optimize the LOAD_PATH with a cache?
|
|
227
|
-
autoload_paths_cache: true, # Should we optimize ActiveSupport autoloads with cache?
|
|
228
|
-
disable_trace: false, # Sets `RubyVM::InstructionSequence.compile_option = { trace_instruction: false }`
|
|
229
|
-
compile_cache_iseq: true, # Should compile Ruby code into ISeq cache?
|
|
230
|
-
compile_cache_yaml: true # Should compile YAML into a cache?
|
|
231
|
-
)
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
**Protip:** You can replace `require 'bootsnap'` with `BootLib::Require.from_gem('bootsnap',
|
|
235
|
-
'bootsnap')` using [this trick](https://github.com/Shopify/bootsnap/wiki/Bootlib::Require). This
|
|
236
|
-
will help optimize boot time further if you have an extremely large `$LOAD_PATH`.
|
|
237
|
-
|
|
238
258
|
## Trustworthiness
|
|
239
259
|
|
|
240
260
|
We use the `*_path_cache` features in production and haven't experienced any issues in a long time.
|
|
241
261
|
|
|
242
|
-
The `compile_cache_*` features work well for us in development on macOS
|
|
243
|
-
|
|
262
|
+
The `compile_cache_*` features work well for us in development on macOS. It should work on Linux,
|
|
263
|
+
and we intend to deploy it in production, but haven't we haven't yet.
|
|
244
264
|
|
|
245
265
|
`disable_trace` should be completely safe, but we don't really use it because some people like to
|
|
246
266
|
use tools that make use of `trace` instructions.
|
|
@@ -250,5 +270,5 @@ use tools that make use of `trace` instructions.
|
|
|
250
270
|
| `load_path_cache` | everywhere |
|
|
251
271
|
| `autoload_path_cache` | everywhere |
|
|
252
272
|
| `disable_trace` | nowhere, but it's safe unless you need tracing |
|
|
253
|
-
| `compile_cache_iseq` | development,
|
|
254
|
-
| `compile_cache_yaml` | development,
|
|
273
|
+
| `compile_cache_iseq` | development, but probably safe to use everywhere |
|
|
274
|
+
| `compile_cache_yaml` | development, but probably safe to use everywhere |
|
data/bootsnap.gemspec
CHANGED
|
@@ -23,12 +23,14 @@ Gem::Specification.new do |spec|
|
|
|
23
23
|
spec.require_paths = ["lib"]
|
|
24
24
|
spec.extensions = ['ext/bootsnap/extconf.rb']
|
|
25
25
|
|
|
26
|
+
spec.required_ruby_version = '>= 2.3.0'
|
|
27
|
+
|
|
26
28
|
spec.add_development_dependency "bundler", '~> 1'
|
|
27
29
|
spec.add_development_dependency 'rake', '~> 10.0'
|
|
28
30
|
spec.add_development_dependency 'rake-compiler', '~> 0'
|
|
29
31
|
spec.add_development_dependency "minitest", "~> 5.0"
|
|
30
32
|
spec.add_development_dependency "mocha", "~> 1.2"
|
|
33
|
+
spec.add_development_dependency "ffi-xattr", "~> 0.1.2"
|
|
31
34
|
|
|
32
35
|
spec.add_runtime_dependency "msgpack", "~> 1.0"
|
|
33
|
-
spec.add_runtime_dependency "snappy", "~> 0.0.15"
|
|
34
36
|
end
|