High performant ruby installations

Most of my clients have no idea about any of this. Ruby, by default, compiles with glibc malloc, which has all kinds of issues.

The reason for this is that it is present in literally every system. Sadly, the ruby-core team refuses to become dependent on any dependencies not part of the operating system (understandable), except for the Rust YJIT compiler written in Rust instead of C. This dependency, however, is optional.

Why would you want to consider jemalloc and YJIT? Memory usage and performance. One project I recently worked on saw memory usage decrease by over 60% after I rolled out Jemalloc to their servers.

Why would you consider Jemalloc, even though it isn't shipped as a default? Let's say you work with a lot of collection type of data where you create things on a background worker. If your workers aren't small enough in their unit of work, they risk bleeding memory. You stand to gain the most if you do much of this work.

To start, we want to install jemalloc, and we want to install rust.

Installing Rust

Rust is installed with the following command:

lang-bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Installing libjemalloc-dev

Then we install jemalloc with:

lang-bash
apt install libjemalloc-dev

Unfortunately, jemalloc isn't available by default. To make jemalloc available everywhere, the easiest I've found is to set the LD_PRELOAD environment variable. `/etc/environment` is a great spot to put this to ensure it is available to all users. Otherwise, ~/.bashrc is enough for the account running ruby processes.

lang-bash
export export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.1

This path is the default for Debian based system. The line above is for Jemalloc installed in Ubuntu 16.04. Most likely, your line should be:

lang-bash
export export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2

Suppose you are on Ubuntu 18.04 and newer. Check which version of libjemalloc.so.n is in the directory /usr/lib/x86_64-linux-gnu/.

Installing ruby

Now to install ruby:

lang-bash
RUBY_CONFIGURE_OPTS="--with-jemalloc --enable-yjit" rbenv install 3.2.2

Verifying our installation

To test if ruby is indeed installed with Jemalloc:

lang-bash
ruby -r rbconfig -e "puts RbConfig::CONFIG['MAINLIBS']"
  #=> -lz -lrt -lrt -ljemalloc -ldl -lcrypt -lm -lpthread

You want to see: -ljemalloc in the result.

To verify that ruby is indeed installed with YJIT

lang-bash
ruby -v --yjit
  #=> ruby 3.2.2 (2023-03-30 revision e51014f9c0) +YJIT [x86_64-linux]

You should see: +YJIT in the result.