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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fcd86efa96cf97561bb9f618b9be5a15394b764f
4
- data.tar.gz: d6855e9fab0efbde45d0da337b2c6e63e4d272b1
3
+ metadata.gz: 3725f53309883dce83894e47eedbb4d8322eabd5
4
+ data.tar.gz: 81e14b1d48fc4431f6ac9bb2729cfb4a69aa0f5c
5
5
  SHA512:
6
- metadata.gz: f4ad490b42d042304a9983fa0468ba202c5c494485e5e22215341c1b07b219a220fedec2c02b3f402a39b94a9eb953b2ba341e5c39fad4bc2681e5d9a5aa3445
7
- data.tar.gz: 898ac7f4a00be925525bcee53ef03be8d9a2c022bff4afe9909f7dee339dd822c71b5c72b4c24a54605bd1c5f73ef342da5d82553b49a243c37351bd007db9f2
6
+ metadata.gz: 5a2b45cb55ceffdb5751561b3a35c476ea29bcd3d3a2a68a46521240d35cc1e61cf9ac90265db751b09967655969c7a685725878b5f80a61fd38a0efd7024458
7
+ data.tar.gz: 7f7975023cb1856d5ccada63e8be2e3ab94a751d4b80121821f2b01df6cbb94f5172041020ebb9300b63760da3fc2776e3b9b56160b95b4b0c07f82fbce07860
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ os: osx
2
+ language: ruby
3
+ rvm: ruby-2.4.0
4
+ before_script: rake
5
+ script: bin/testunit
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 ![CI status](https://travis-ci.org/Shopify/bootsnap.svg?branch=master)
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 ruby and (optionally) `ActiveSupport` and `YAML`
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
- ![How path searching works](https://cloud.githubusercontent.com/assets/3074765/24532143/18278cd6-158c-11e7-8250-78d831df70db.png)
110
+ ![How path searching works](https://cloud.githubusercontent.com/assets/3074765/25388270/670b5652-299b-11e7-87fb-975647f68981.png)
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 simpler implementation of this concept can be found in [yomikomu](https://github.com/ko1/yomikomu)).*
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 using `xattr`s on the source files. This is likely to change in
96
- the future, as it has some limitations (notably precluding Linux support except where the user feels
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
- fgetxattr n
120
- fgetxattr n
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
- Bootsnap writes two `xattrs` attached to each file read:
168
+ This may look worse at a glance, but underlies a large performance difference.
125
169
 
126
- * `user.aotcc.value`, the binary compilation result; and
127
- * `user.aotcc.key`, a cache key to determine whether `user.aotcc.value` is still valid.
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
- The key includes several fields:
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
- * `data_size`, the number of bytes in `user.aotcc.value`, which we need to read it into a buffer
134
- using `fgetxattr(2)`;
135
- * `ruby_revision`, the version of Ruby this was compiled with; and
136
- * `mtime`, the last-modification timestamp of the source file when it was compiled.
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
- ![Compilation Caching](https://burkelibbey.s3.amazonaws.com/bootsnap-compile-cache.png)
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
- fgetxattr n
189
- fgetxattr n
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, but probably don't work on
243
- Linux at all.
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, unlikely to work on Linux |
254
- | `compile_cache_yaml` | development, unlikely to work on Linux |
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
data/dev.yml CHANGED
@@ -1,3 +1,6 @@
1
+ env:
2
+ BOOTSNAP_PEDANTIC: '1'
3
+
1
4
  up:
2
5
  - ruby: 2.3.3
3
6
  - bundler