A quick win that made loading faker-ruby 19% faster.
faker has lots of generators, which is good! People love using them and we want to make the library even better (more performant, easier to use and maintain).
This year, we decided to focus on optimizing the library for both speed and usability. In the meantime, we found some low-hanging fruit to speed things up.
Inspired by byroot’s work on optimizing Ruby’s JSON, we started by revamping the benchmark suite and profiling.
Revamping faker benchmark suite
We started running a benchmark as part of CI.
After all, tweaking code seeking performance gains can sometimes lead to worse outcomes, so it’s a good practice to compare benchmark results when making any changes.
The PR has a good description and commit messages to follow along. The main lesson here was to group the benchmark-related gems to only install them when needed. And we sped CI up by caching gems automatically with bundler-cache: true, the recommended way to cache dependencies using GitHub workflows. Yay for faster CI!
Profiling faker-ruby with Vernier
With benchmark scripts in place, we were in a good place for the next step: profiling. We used Vernier.
Vernier is cool because you can share the results publicly, if you want to. Here are a few examples from profiling faker:
- bundle exec vernier run – ruby -e “require ‘faker’”
- bundle exec vernier run –interval 100 –allocation-interval 10 – ruby -e “require ‘faker’”
- bundle exec vernier run –interval 100 –allocation-interval 10 – ruby -e “require ‘faker’; Faker::Internet.email”
The profiler samples show us what is going on during load time:
Call Tree results when loading faker
Stack Chart results when loading faker
With these samples, we immediately noticed that OpenSSL was responsible for about 9% of the samples.
OpenSSL is only used by 3 niche generators and setting this library up is costly. Not requiring it during load time would speed up faker for most users.
We experimented with only requiring openssl/digest but that didn’t work. Lots of tests broke, so we switched gears. What if OpenSSL was only loaded at runtime when needed instead of every time?
And that’s what we did: by autoloading OpenSSL, we got these results:
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
require openssl 1.000 i/100ms
autoload openssl 1.000 i/100ms
Calculating -------------------------------------
require openssl 10.485 (± 0.0%) i/s (95.37 ms/i) - 53.000 in 5.057875s
autoload openssl 12.429 (± 0.0%) i/s (80.46 ms/i) - 63.000 in 5.072235s
Comparison:
require openssl: 10.5 i/s
autoload openssl: 12.4 i/s - 1.19x faster
This change made faker load ~19% faster 🚀 We can see how much cleaner the profiler samples look after the change:
loading faker profiler after autoload call tree results
loading faker profiler after autoload stack chart results
We have other potential improvements in mind based on this exploration.
This shows how important it is to profile your code because that’s how you can know for sure what is going on. Plus, having the benchmark suite ready ensures you’re not introducing any regressions along the way.
In this case, autoloading made sense and it was a quick win. It might not work for every library where the dependencies need to be there, but it was great that we could take advantage of it.
This improvement is available on faker’s latest release, v3.6.0.
Happy coding!