Skip to content

Commit f88650e

Browse files
Document lazy load experiment results
Closes #3199 Co-Authored-By: Thiago Araujo <thd.araujo@gmail.com>
1 parent 5bd5b8e commit f88650e

File tree

1 file changed

+101
-0
lines changed

1 file changed

+101
-0
lines changed

experiments/lazy_load.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Lazy load experiment results
2+
3+
- Git Branch: [sb-ta/lazy-load-experiment](https://github.com/faker-ruby/faker/compare/main...sb-ta/lazy-load-experiment)
4+
- Date: February 10th, 2026
5+
- Owner(s): Stefanni Brasil and Thiago Araujo
6+
7+
## Impact
8+
9+
Using `const_missing` to lazy load generators was an idea from talking with Jeremy Evans, who kindly responded our questions about improving faker's performance.
10+
11+
### Changes needed
12+
13+
#### Namespace changes
14+
15+
A few generators had namespaces that didn't match their file name, and most of them were manageable, but two needed to be renamed:
16+
17+
- `Faker::Music` -> `Faker::Song`. `Faker::Music` is a nested namespace.
18+
- `Faker::Internet::HTTP` -> `Faker::HTTP`. `Faker::Internet` is another generator.
19+
20+
Considering that only two generators would be renamed, it wouldn't be a huge burden for users to use this approach.
21+
22+
#### File location changes
23+
24+
To prevent other generators from erroring out due to namespace clashing, some generators have to be moved around (ex. `Faker::Quote` was moved from `/faker/quotes/quote` to `faker/default/quote`). Users can still use the generators as before, their namespaces didn't change.
25+
26+
### Benefits
27+
28+
- no additional dependencies needed
29+
- code is extremely faster
30+
- after changing these two generators, which would be breaking changes, we can enable this as an opt-in configuration
31+
32+
## Results
33+
34+
profiler:
35+
36+
- [bundle exec vernier run -- ruby -e "require 'faker'" LAZY_LOAD=1](https://share.firefox.dev/3ZuCP55)
37+
- [bundle exec vernier run --interval 100 --allocation-interval 10 -- ruby -e "require 'faker'; Faker::Internet.email" LAZY_LOAD=1](https://share.firefox.dev/4601PoA)
38+
39+
benchmarks (Machine specs: Apple M1 Pro 16GB memory on MacOS Sequoia 15.7.3.)
40+
41+
```sh
42+
benchmark % ruby require.rb
43+
took 250.0249999575317ms to load
44+
```
45+
46+
```sh
47+
benchmark % ruby load.rb
48+
ruby 3.3.10 (2025-10-23 revision 343ea05002) [arm64-darwin24]
49+
Warming up --------------------------------------
50+
require 1.000 i/100ms
51+
lazyload 1.000 i/100ms
52+
Calculating -------------------------------------
53+
require 5.874 (± 0.0%) i/s (170.25 ms/i) - 30.000 in 5.115652s
54+
lazyload 12.207 (± 8.2%) i/s (81.92 ms/i) - 61.000 in 5.007059s
55+
56+
Comparison:
57+
require: 5.9 i/s
58+
lazyload: 12.2 i/s - 2.08x faster
59+
```
60+
61+
## Artifacts
62+
63+
Constants were configured to be lazy loaded using this script:
64+
65+
```ruby
66+
# lazy_load.rb
67+
68+
CATEGORIES = {
69+
Blockchain: 'blockchain',
70+
Books: 'books',
71+
Creature: 'creature',
72+
Default: 'default',
73+
Fantasy: 'fantasy',
74+
Games: 'games',
75+
JapaneseMedia: 'japanese_media',
76+
Locations: 'locations',
77+
Movies: 'movies',
78+
Music: 'music',
79+
Quotes: 'quotes',
80+
Religion: 'religion',
81+
Sports: 'sports',
82+
Travel: 'travel',
83+
TvShows: 'tv_shows'
84+
}.freeze
85+
86+
def template(key)
87+
"# frozen_string_literal: true
88+
89+
module Faker
90+
class #{key}
91+
if ENV['LAZY_LOAD'] == '1'
92+
Faker.lazy_load(self)
93+
end
94+
end
95+
end"
96+
end
97+
98+
CATEGORIES.each do |key, value|
99+
File.write(File.join('lib', 'faker', "#{value}.rb"), template(key))
100+
end
101+
```

0 commit comments

Comments
 (0)