[#98098] [Ruby master Feature#16824] Follow RubyGems naming conventions for the stdlib — shannonskipper@...

Issue #16824 has been reported by shan (Shannon Skipper).

14 messages 2020/05/01

[#98147] [Ruby master Feature#16832] Use #name rather than #inspect to build "uninitialized constant" error messages — jean.boussier@...

Issue #16832 has been reported by byroot (Jean Boussier).

20 messages 2020/05/06

[#98174] [Ruby master Bug#16837] Can we make Ruby 3.0 as fast as Ruby 2.7 with the new assertions? — takashikkbn@...

Issue #16837 has been reported by k0kubun (Takashi Kokubun).

10 messages 2020/05/07

[#98241] [Ruby master Bug#16845] Building Ruby with old existing system Ruby results in make error with ./tool/file2lastrev.rb — erik@...

Issue #16845 has been reported by ErikSwan (Erik Swan).

7 messages 2020/05/09

[#98256] [Ruby master Feature#16847] Cache instruction sequences by default — jean.boussier@...

Issue #16847 has been reported by byroot (Jean Boussier).

16 messages 2020/05/11

[#98257] [Ruby master Feature#16848] Allow callables in $LOAD_PATH — jean.boussier@...

Issue #16848 has been reported by byroot (Jean Boussier).

27 messages 2020/05/11

[#98318] [Ruby master Bug#16853] calling bla(hash, **kw) with a string-based hash passes the strings into **kw (worked < 2.7) [email protected]

Issue #16853 has been reported by sylvain.joyeux (Sylvain Joyeux).

12 messages 2020/05/13

[#98355] [Ruby master Bug#16889] TracePoint.enable { ... } also activates the TracePoint for other threads, even outside the block — eregontp@...

Issue #16889 has been reported by Eregon (Benoit Daloze).

16 messages 2020/05/14

[#98363] [Ruby master Feature#16891] Restore Positional Argument to Keyword Conversion — merch-redmine@...

Issue #16891 has been reported by jeremyevans0 (Jeremy Evans).

23 messages 2020/05/14

[#98371] [Ruby master Feature#16894] Integer division for Ruby 3 — andrew@...

Issue #16894 has been reported by ankane (Andrew Kane).

18 messages 2020/05/15

[#98391] [Ruby master Bug#16896] MakeMakefile methods should be private — eregontp@...

Issue #16896 has been reported by Eregon (Benoit Daloze).

10 messages 2020/05/15

[#98396] [Ruby master Feature#16897] Can a Ruby 3.0 compatible general purpose memoizer be written in such a way that it matches Ruby 2 performance? — sam.saffron@...

Issue #16897 has been reported by sam.saffron (Sam Saffron).

25 messages 2020/05/16

[#98453] [Ruby master Bug#16904] rubygems: psych: superclass mismatch for class Mark (TypeError) — jaruga@...

Issue #16904 has been reported by jaruga (Jun Aruga).

18 messages 2020/05/20

[#98486] [Ruby master Bug#16908] Strange behaviour of Hash#shift when used with `default_proc`. — samuel@...

Issue #16908 has been reported by ioquatix (Samuel Williams).

14 messages 2020/05/23

[#98569] [Ruby master Bug#16921] s390x: ramdom test failures for timeout or segmentation fault — jaruga@...

Issue #16921 has been reported by jaruga (Jun Aruga).

9 messages 2020/05/29

[#98599] [Ruby master Bug#16926] Kernel#require does not load a feature twice when $LOAD_PATH has been modified spec fails only on 2.7 — eregontp@...

Issue #16926 has been reported by Eregon (Benoit Daloze).

12 messages 2020/05/31

[ruby-core:98529] [Ruby master Feature#16848] Allow callables in $LOAD_PATH

From: ko1@...
Date: 2020-05-26 14:01:42 UTC
List: ruby-core #98529
Issue #16848 has been updated by ko1 (Koichi Sasada).


> $LOAD_PATH as a "view"

This is what I proposed at comment #16.

Maybe there are two strategies:

(1) extend $LOAD_PATH.
(2) introduce $LOAD_PATH2 and deprecate $LOAD_PATH.
(3) other great idea?

Several years ago I tried this topic when I made an `ISeq#to_bianry`, but I gave up because of complexity.
It is good time to have a nice approach.

FYI: [ruby-core:46896] (Half-baked DRAFT) new `require' framework


----------------------------------------
Feature #16848: Allow callables in $LOAD_PATH
https://bugs.ruby-lang.org/issues/16848#change-85816

* Author: byroot (Jean Boussier)
* Status: Feedback
* Priority: Normal
----------------------------------------
Make it easier to implement `$LOAD_PATH` caching, and speed up application boot time.

I benchmarked it on Redmine's master using bootsnap with only the optimization enabled:

```ruby
if ENV['CACHE_LOAD_PATH']
  require 'bootsnap'
  Bootsnap.setup(
    cache_dir:            'tmp/cache',
    development_mode:     false,
    load_path_cache:      true,
    autoload_paths_cache: true,
    disable_trace:        false,
    compile_cache_iseq:   true,
    compile_cache_yaml:   false,
  )
end
```

```
$ RAILS_ENV=production time bin/rails runner 'p 1'
        2.66 real         1.99 user         0.66 sys
$ RAILS_ENV=production time bin/rails runner 'p 1'
        2.71 real         1.97 user         0.66 sys
$ CACHE_LOAD_PATH=1 RAILS_ENV=production time bin/rails runner 'p 1'
        1.41 real         1.12 user         0.28 sys
$ CACHE_LOAD_PATH=1 RAILS_ENV=production time bin/rails runner 'p 1'
        1.41 real         1.12 user         0.28 sys
```

That's twice for a relatively small application. And the performance improvement is not linear; the larger the application, the larger the improvement.

### How it works

`require` has `O($LOAD_PATH.size)` performance. The more gems you add to your `Gemfile`, the larger `$LOAD_PATH` becomes. `require "foo.rb"` will try to open the file in each of the `$LOAD_PATH` entries. And since more gems usually also means more `require` calls, loading Ruby code may take up to quadratic performance loss.

To improve this, Bootsnap pre-computes a map of all the files in your `$LOAD_PATH`, and uses it to convert relative paths into absolute paths so that Ruby skips the `$LOAD_PATH` traversal.

```ruby
$LOAD_PATH = $w(/gems/foo/lib /gems/bar/lib)

BOOTSNAP_CACHE = {
  "bar.rb" => "/gems/bar/lib/bar.rb",
}
```

This resolves file lookup by a single hash lookup, and reduces boot performance from roughly `O($LOAD_PATH.size * number_of_files_to_require)` to `O(number_of_files_to_require)`.

This optimization is also used in [Gel](https://github.com/gel-rb/gel), a Rubygems/Bundler replacement.

### Trade offs

Every time `$LOAD_PATH` is modified, the cache must become invalidated. While this is complex to do for Bootsnap, it would be fairly easy if it is implemented inside Ruby.

More importantly, you have to invalidate the cache whenever you add or delete a file to/from one of the `$LOAD_PATH` members; otherwise, if you shadow or unshadow another file farther in the `$LOAD_PATH`, Bootsnap will load a wrong file. For instance, if `require "foo.rb"` initially resolves to `/some/gem/foo.rb`, and you create `lib/foo.rb`, you'll need to flush Bootsnap cache.

That latter is trickier, and Bootsnap has decided that it is rare enough to cause actual problems, and so far that holds. But that is not a trade off Ruby can make.

However that's probably a tradeoff Rubygems/Bundler can make. While it's common to edit your gems to debug something, it's really uncommon to add or remove files inside them. So in theory Rubygems/Bundler could compute a map of all files in a gem that can be required after it installs it. Then when you activate it, you merge it together with the other activated gems.

### Proposal

This could be reasonably easy to implement if `$LOAD_PATH` accepted callables in addition to paths. Something like this:

```ruby
$LOAD_PATH = [
  'my_app/lib',
  BundlerOrRubygems.method(:lookup),
]
```

The contract would be that `BundlerOrRubygems.lookup("some_relative/path.rb")` would return either an absolute path or `nil`. With such API, it would be easy to cache absolute paths only for gems and the stdlib, and preserve the current cache-less behavior for the application specific load paths, which are usually much less numerous. It would also allow frameworks such as Rails to implement the same caching for application paths when running in an environment
where the source files are immutable (typically production).



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:[email protected]?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>

In This Thread