4 ms·
so it resorts to a conservative-and-expensive search mechanism in order to preserve those semantics. Sorry but come on, these apologisms are part of the reason
by moe 13y ago
so it resorts to a conservative-and-expensive search mechanism in order to preserve those semantics.
Sorry but come on, these apologisms are part of the reason why these things never get fixed. Ruby 1.8 went end-of-life two years ago. And which precious ambiguousity are we enshrining here that could possibly justify this magnitude of wasted developer lifetime?
There is no excuse. If anyone in charge gave a flying f*ck and was not a complete code illiterate we'd have a --fast flag by now. In fact we'd probably not even have it anymore because it would have become the de facto default over night...
- cheald 13y agoWow, there's some hostility there. The bundler team has accepted performance pull requests from me before - I'm sure they'd be happy to do the same if you were to add this --fast flag. Perhaps you should show them what code literacy looks like. The issue is the require means "search the load paths for files matching this filename, filename.rb, filename.so, then search relative to the requiring file for the same if nothing was found". Bundler manages loads by finding all the gems in the bundle (which have been pre-resolved and locked), then creating a load path which satisfies the dependency order. So, when you `require "foo"`, the (extensively large) load path is iterated looking for a match. Given the number of gems (and thus the number of items in the load path) in a typical project, it's not difficult to recognize why this might be slow. Over the course of a load, you will perform hundreds or thousands of require invocations, each of which has to perform this iterative search. This seems unnecessary, except you have to know how to resolve ambiguities. Consider something like this file: https://github.com/rails/docrails/blob/master/activesupport/lib/active_support/core_ext/benchmark.rb https://github.com/rails/docrails/blob/master/activesupport/... What does that require statement mean? It's a require statement that asks Ruby to require something called 'benchmark', from within a directory that contains a 'benchmark' file. There are, in fact, multiple matches for "benchmark" in the load path. In order to ensure that the right one is loaded, you have to perform that iterative search, in order, to make sure that you always arrive at the stdlib "benchmark" before the ActiveSupport core_ext "benchmark". Yet, in the majority of cases, by convention, when you require some file in a library, you're actually wanting to require some file relative to your requiring file. It's obviously inefficient to iterate the entire load path looking for some file that you know lives in a subdirectory relative to the requiring file, but since Ruby doesn't have any way of knowing whether you mean "load the first hit of this file from the load path" or "load the file relative to the requiring file", it has to perform the slower, more conservative search. This is what require_relative is for. That lets you tell Ruby "Hey, skip all the load path shenanigans and just load this file relative to the requiring file". Since you don't have to do the extensive load path iteration, the search time is constant, rather than linear. I've experimented with a few "--fast" systems, including pre-emptive load path iteration and caching (doesn't really make a difference; you aren't going to beat stat by any significant margin), and load path "baking" (for each file loaded in the manifest, keep the resolved absolute path and replace relative paths with absolute paths"), but I've yet to come up with a solution that properly replicates Ruby's search-and-load behavior, and invariably ends up breaking on some edge case. Fuzzy semantics are kind of a bitch like that.
- moe 13y agoWow, there's some hostility there. Yes. I haven't interacted with the bundler team but I did try to contribute to rubygems during their security debacle. I hope these are not the same people. but I've yet to come up with a solution that properly replicates Ruby's search-and-load behavior, and invariably ends up breaking on some edge case. I have a hard time believing that'd be your only bottleneck? If that's really all then do exactly what you said. Break the edge case, replace your iterative search with the O(1) lookup. There you have your --fast flag. Document that require_relative is to be used for relative imports for the flag to work. Then count the days until someone patches up a 'fast-rails'. And to the gem that auto-converts projects into --fast compatibility.