A train departing from a station

Making faker-ruby faster with autoloading

By

Stefanni Brasil and Thiago Araujo

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:

The profiler samples show us what is going on during load time:

loading faker profiler call tree results

Call Tree results when loading faker

loading faker profiler stack chart results

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 call tree results

loading faker profiler after autoload stack chart 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!

Did you like this article? Then you're gonna love these other ones: