bootsnap 0.2.15 → 1.1.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/CHANGELOG.md +27 -0
- data/README.md +59 -43
- data/bootsnap.gemspec +8 -5
- data/ext/bootsnap/bootsnap.c +575 -394
- data/ext/bootsnap/bootsnap.h +1 -3
- data/lib/bootsnap/compile_cache/iseq.rb +10 -13
- data/lib/bootsnap/compile_cache/yaml.rb +4 -1
- data/lib/bootsnap/compile_cache.rb +5 -6
- data/lib/bootsnap/load_path_cache/cache.rb +25 -4
- data/lib/bootsnap/load_path_cache/path.rb +21 -11
- data/lib/bootsnap/load_path_cache/path_scanner.rb +0 -3
- data/lib/bootsnap/load_path_cache/store.rb +1 -1
- data/lib/bootsnap/setup.rb +47 -0
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +1 -0
- metadata +5 -18
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fbdf0cc19b09b396571d9d38cb037cebfcdab7b2
|
|
4
|
+
data.tar.gz: f07f07ec071dbffe3a48e1ae324457729a11ca61
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9db4ab87163f7c05a7b5c62fe17617149ec5acdc1caf186013c334222a4ad0a2087c5992b44fcf424201dd5b12448ce25826ccb4f99f495e450437aa90d0a0ae
|
|
7
|
+
data.tar.gz: 84714b27f6a3be1650807af151309dc789390944c1a317ec9c246067c2f4299184c182aa878c8eb5715da989c1c768a3d86992af02ab9d974f1d26d09bb2d170
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
# 1.1.0
|
|
2
|
+
|
|
3
|
+
* Add `bootsnap/setup`
|
|
4
|
+
* Support jruby (without compile caching features)
|
|
5
|
+
* Better deoptimization when Coverage is enabled
|
|
6
|
+
* Consider `Bundler.bundle_path` to be stable
|
|
7
|
+
|
|
8
|
+
# 1.0.0
|
|
9
|
+
|
|
10
|
+
* (none)
|
|
11
|
+
|
|
12
|
+
# 0.3.2
|
|
13
|
+
|
|
14
|
+
* Minor performance savings around checking validity of cache in the presence of relative paths.
|
|
15
|
+
* When coverage is enabled, skips optimization instead of exploding.
|
|
16
|
+
|
|
17
|
+
# 0.3.1
|
|
18
|
+
|
|
19
|
+
* Don't whitelist paths under `RbConfig::CONFIG['prefix']` as stable; instead use `['libdir']` (#41).
|
|
20
|
+
* Catch `EOFError` when reading load-path-cache and regenerate cache.
|
|
21
|
+
* Support relative paths in load-path-cache.
|
|
22
|
+
|
|
23
|
+
# 0.3.0
|
|
24
|
+
|
|
25
|
+
* Migrate CompileCache from xattr as a cache backend to a cache directory
|
|
26
|
+
* Adds support for Linux and FreeBSD
|
|
27
|
+
|
|
1
28
|
# 0.2.15
|
|
2
29
|
|
|
3
30
|
* Support more versions of ActiveSupport (`depend_on`'s signature varies; don't reiterate it)
|
data/README.md
CHANGED
|
@@ -1,39 +1,46 @@
|
|
|
1
|
-
# Bootsnap ](https://travis-ci.org/Shopify/bootsnap)
|
|
2
2
|
|
|
3
3
|
**Beta-quality. See [the last section of this README](#trustworthiness).**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Bootsnap is a library that plugs into Ruby, with optional support for `ActiveSupport` and `YAML`,
|
|
6
|
+
to optimize and cache expensive computations. See [How Does This Work](#how-does-this-work).
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
methods to optimize and cache expensive computations. See [the How Does This Work section](#how-does-this-work) for more information.
|
|
10
|
-
|
|
11
|
-
#### Quick Performance Overview
|
|
8
|
+
#### Performance
|
|
12
9
|
- [Discourse](https://github.com/discourse/discourse) reports a boot time reduction of approximately 50%, from roughly 6 to 3 seconds on one machine;
|
|
13
10
|
- One of our smaller internal apps also sees a reduction of 50%, from 3.6 to 1.8 seconds;
|
|
14
11
|
- The core Shopify platform -- a rather large monolithic application -- boots about 75% faster, dropping from around 25s to 6.5s.
|
|
15
12
|
|
|
16
13
|
## Usage
|
|
17
14
|
|
|
15
|
+
This gem works on MacOS and Linux.
|
|
16
|
+
|
|
18
17
|
Add `bootsnap` to your `Gemfile`:
|
|
19
18
|
|
|
20
19
|
```ruby
|
|
21
20
|
gem 'bootsnap'
|
|
22
21
|
```
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
If you are using rails, add this to `config/boot.rb` immediately after `require 'bundler/setup'`:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
require 'bootsnap/setup'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If you are not using rails, or if you are but want more control over things, add this to your
|
|
30
|
+
application setup immediately after `require 'bundler/setup'` (i.e. as early as possible: the sooner
|
|
31
|
+
this is loaded, the sooner it can start optimizing things)
|
|
26
32
|
|
|
27
33
|
```ruby
|
|
28
34
|
require 'bootsnap'
|
|
35
|
+
env = ENV['RAILS_ENV'] || "development"
|
|
29
36
|
Bootsnap.setup(
|
|
30
|
-
cache_dir: 'tmp/cache',
|
|
31
|
-
development_mode:
|
|
32
|
-
load_path_cache: true,
|
|
33
|
-
autoload_paths_cache: true,
|
|
34
|
-
disable_trace:
|
|
35
|
-
compile_cache_iseq: true,
|
|
36
|
-
compile_cache_yaml: true
|
|
37
|
+
cache_dir: 'tmp/cache', # Path to your cache
|
|
38
|
+
development_mode: env == 'development', # Current working environment, e.g. RACK_ENV, RAILS_ENV, etc
|
|
39
|
+
load_path_cache: true, # Optimize the LOAD_PATH with a cache
|
|
40
|
+
autoload_paths_cache: true, # Optimize ActiveSupport autoloads with cache
|
|
41
|
+
disable_trace: true, # (Alpha) Set `RubyVM::InstructionSequence.compile_option = { trace_instruction: false }`
|
|
42
|
+
compile_cache_iseq: true, # Compile Ruby code into ISeq cache, breaks coverage reporting.
|
|
43
|
+
compile_cache_yaml: true # Compile YAML into a cache
|
|
37
44
|
)
|
|
38
45
|
```
|
|
39
46
|
|
|
@@ -43,8 +50,7 @@ will help optimize boot time further if you have an extremely large `$LOAD_PATH`
|
|
|
43
50
|
|
|
44
51
|
## How does this work?
|
|
45
52
|
|
|
46
|
-
Bootsnap
|
|
47
|
-
methods. These methods are modified to cache results of expensive computations, and can be grouped
|
|
53
|
+
Bootsnap optimizes methods to cache results of expensive computations, and can be grouped
|
|
48
54
|
into two broad categories:
|
|
49
55
|
|
|
50
56
|
* [Path Pre-Scanning](#path-pre-scanning)
|
|
@@ -104,7 +110,8 @@ entries do not expire -- once their contents has been scanned, it is assumed to
|
|
|
104
110
|
|
|
105
111
|
The only directories considered "stable" are things under the Ruby install prefix
|
|
106
112
|
(`RbConfig::CONFIG['prefix']`, e.g. `/usr/local/ruby` or `~/.rubies/x.y.z`), and things under the
|
|
107
|
-
`Gem.path` (e.g. `~/.gem/ruby/x.y.z`). Everything else is considered
|
|
113
|
+
`Gem.path` (e.g. `~/.gem/ruby/x.y.z`) or `Bundler.bundle_path`. Everything else is considered
|
|
114
|
+
"volatile".
|
|
108
115
|
|
|
109
116
|
In addition to the [`Bootsnap::LoadPathCache::Cache`
|
|
110
117
|
source](https://github.com/Shopify/bootsnap/blob/master/lib/bootsnap/load_path_cache/cache.rb),
|
|
@@ -120,7 +127,8 @@ result too, raising a `LoadError` without touching the filesystem at all.
|
|
|
120
127
|
|
|
121
128
|
### Compilation Caching
|
|
122
129
|
|
|
123
|
-
*(A
|
|
130
|
+
*(A more readable implementation of this concept can be found in
|
|
131
|
+
[yomikomu](https://github.com/ko1/yomikomu)).*
|
|
124
132
|
|
|
125
133
|
Ruby has complex grammar and parsing it is not a particularly cheap operation. Since 1.9, Ruby has
|
|
126
134
|
translated ruby source to an internal bytecode format, which is then executed by the Ruby VM. Since
|
|
@@ -134,9 +142,8 @@ implementation. We use the same strategy of compilation caching for YAML documen
|
|
|
134
142
|
equivalent of Ruby's "bytecode" format being a MessagePack document (or, in the case of YAML
|
|
135
143
|
documents with types unsupported by MessagePack, a Marshal stream).
|
|
136
144
|
|
|
137
|
-
These compilation results are stored
|
|
138
|
-
the
|
|
139
|
-
like changing mount flags). However, this is a very performant implementation.
|
|
145
|
+
These compilation results are stored in a cache directory, with filenames generated by taking a hash
|
|
146
|
+
of the full expanded path of the input file (FNV1a-64).
|
|
140
147
|
|
|
141
148
|
Whereas before, the sequence of syscalls generated to `require` a file would look like:
|
|
142
149
|
|
|
@@ -158,32 +165,36 @@ With bootsnap, we get:
|
|
|
158
165
|
```
|
|
159
166
|
open /c/foo.rb -> n
|
|
160
167
|
fstat64 n
|
|
161
|
-
|
|
162
|
-
|
|
168
|
+
close n
|
|
169
|
+
open /c/foo.rb -> n
|
|
170
|
+
fstat64 n
|
|
171
|
+
open (cache) -> m
|
|
172
|
+
read m
|
|
173
|
+
read m
|
|
174
|
+
close m
|
|
163
175
|
close n
|
|
164
176
|
```
|
|
165
177
|
|
|
166
|
-
|
|
178
|
+
This may look worse at a glance, but underlies a large performance difference.
|
|
167
179
|
|
|
168
|
-
* `
|
|
169
|
-
|
|
180
|
+
*(The first three syscalls in both listings -- `open`, `fstat64`, `close` -- are not inherently
|
|
181
|
+
useful. [This ruby patch](https://bugs.ruby-lang.org/issues/13378) optimizes them out when coupled
|
|
182
|
+
with bootsnap.)*
|
|
170
183
|
|
|
171
|
-
|
|
184
|
+
Bootsnap writes a cache file containing a 64 byte header followed by the cache contents. The header
|
|
185
|
+
is a cache key including several fields:
|
|
172
186
|
|
|
173
187
|
* `version`, hardcoded in bootsnap. Essentially a schema version;
|
|
188
|
+
* `os_version`, A hash of the current kernel version (on macOS, BSD) or glibc version (on Linux);
|
|
174
189
|
* `compile_option`, which changes with `RubyVM::InstructionSequence.compile_option` does;
|
|
175
|
-
* `
|
|
176
|
-
|
|
177
|
-
* `
|
|
178
|
-
* `
|
|
190
|
+
* `ruby_revision`, the version of Ruby this was compiled with;
|
|
191
|
+
* `size`, the size of the source file;
|
|
192
|
+
* `mtime`, the last-modification timestamp of the source file when it was compiled; and
|
|
193
|
+
* `data_size`, the number of bytes following the header, which we need to read it into a buffer.
|
|
179
194
|
|
|
180
195
|
If the key is valid, the result is loaded from the value. Otherwise, it is regenerated and clobbers
|
|
181
196
|
the current cache.
|
|
182
197
|
|
|
183
|
-
This diagram may help illustrate how it works:
|
|
184
|
-
|
|
185
|
-

|
|
186
|
-
|
|
187
198
|
### Putting it all together
|
|
188
199
|
|
|
189
200
|
Imagine we have this file structure:
|
|
@@ -227,8 +238,13 @@ With bootsnap, we get:
|
|
|
227
238
|
```
|
|
228
239
|
open /c/foo.rb -> n
|
|
229
240
|
fstat64 n
|
|
230
|
-
|
|
231
|
-
|
|
241
|
+
close n
|
|
242
|
+
open /c/foo.rb -> n
|
|
243
|
+
fstat64 n
|
|
244
|
+
open (cache) -> m
|
|
245
|
+
read m
|
|
246
|
+
read m
|
|
247
|
+
close m
|
|
232
248
|
close n
|
|
233
249
|
```
|
|
234
250
|
|
|
@@ -253,8 +269,8 @@ open /c/nope.bundle -> -1
|
|
|
253
269
|
|
|
254
270
|
We use the `*_path_cache` features in production and haven't experienced any issues in a long time.
|
|
255
271
|
|
|
256
|
-
The `compile_cache_*` features work well for us in development on macOS
|
|
257
|
-
|
|
272
|
+
The `compile_cache_*` features work well for us in development on macOS. It should work on Linux,
|
|
273
|
+
and we intend to deploy it in production, but we haven't yet.
|
|
258
274
|
|
|
259
275
|
`disable_trace` should be completely safe, but we don't really use it because some people like to
|
|
260
276
|
use tools that make use of `trace` instructions.
|
|
@@ -264,5 +280,5 @@ use tools that make use of `trace` instructions.
|
|
|
264
280
|
| `load_path_cache` | everywhere |
|
|
265
281
|
| `autoload_path_cache` | everywhere |
|
|
266
282
|
| `disable_trace` | nowhere, but it's safe unless you need tracing |
|
|
267
|
-
| `compile_cache_iseq` | development,
|
|
268
|
-
| `compile_cache_yaml` | development,
|
|
283
|
+
| `compile_cache_iseq` | development, but probably safe to use everywhere |
|
|
284
|
+
| `compile_cache_yaml` | development, but probably safe to use everywhere |
|
data/bootsnap.gemspec
CHANGED
|
@@ -18,19 +18,22 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
19
19
|
f.match(%r{^(test|spec|features)/})
|
|
20
20
|
end
|
|
21
|
-
spec.bindir = "exe"
|
|
22
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
23
21
|
spec.require_paths = ["lib"]
|
|
24
|
-
spec.extensions = ['ext/bootsnap/extconf.rb']
|
|
25
22
|
|
|
26
|
-
spec.required_ruby_version = '>= 2.
|
|
23
|
+
spec.required_ruby_version = '>= 2.0.0'
|
|
24
|
+
|
|
25
|
+
if RUBY_PLATFORM =~ /java/
|
|
26
|
+
spec.platform = 'java'
|
|
27
|
+
else
|
|
28
|
+
spec.platform = Gem::Platform::RUBY
|
|
29
|
+
spec.extensions = ['ext/bootsnap/extconf.rb']
|
|
30
|
+
end
|
|
27
31
|
|
|
28
32
|
spec.add_development_dependency "bundler", '~> 1'
|
|
29
33
|
spec.add_development_dependency 'rake', '~> 10.0'
|
|
30
34
|
spec.add_development_dependency 'rake-compiler', '~> 0'
|
|
31
35
|
spec.add_development_dependency "minitest", "~> 5.0"
|
|
32
36
|
spec.add_development_dependency "mocha", "~> 1.2"
|
|
33
|
-
spec.add_development_dependency "ffi-xattr", "~> 0.1.2"
|
|
34
37
|
|
|
35
38
|
spec.add_runtime_dependency "msgpack", "~> 1.0"
|
|
36
39
|
end
|