Some of you might know that we are bootstrapped with a shoestring budget.

This means that we are frugal about how we budget for resources and buy-into only more carefully. Which is great because then our end users get to enjoy performance of a well tuned app top down.

Through this post we explain how we used jemalloc—a memory allocator—to improve performance of our Rails application. It helped us reduce our RAM usage by 30%.

But first, what is jemalloc?

jemalloc is a general purpose malloc(3) implementation that emphasizes fragmentation avoidance and scalable concurrency support.

By default Ruby uses the glibc for memory allocation. Switching over to jemalloc has been discussed often by experts in the Ruby/Rails community but a decision is yet to come. We are hoping this thought will see the light of the day soon and jemalloc will be considered by the Ruby core group.

It is worthwhile to mention here that several large projects and organizations like Redis, GitHub, GitLab and Discourse are already using jemalloc on production.

Jemalloc

jemalloc is a general purpose malloc(3) implementation that emphasizes fragmentation avoidance and scalable concurrency support.

While it will take some time for ruby aficionados to arrive on this decision, we decided to take full advantage of what is available immediately. Turns out, by compiling Ruby with jemalloc we were able to reduce memory usage of our Rails application quite drastically.

An improvement of ~28.62% (>1 Gb RAM) on memory usage was registered on our production server (Linode), but YMMV.

Here are our before and after stats:

# ssh into your server # before jemalloc: $ free m total used free shared buff/cache available Mem: 8162148 4126424 2439396 282816 1596328 3604716 Swap: 524284 90064 434220 # after jemalloc $ free m total used free shared buff/cache available Mem: 8162148 2945404 3157684 282840 2059060 4785128 Swap: 524284 85200 439084

A single instance of Linode with ~8Gb RAM is at our disposal.

How to install:

To install jemalloc on your linux server (Ubuntu 16.04.1 LTS):

ssh into your server or on your dev machine.

$ sudo apt-get update $ sudo apt-get install libjemalloc-dev # Install jemalloc

On macOS, you can use homebrew to install jemalloc, like so:

$ brew install jemalloc

Once jemalloc is set up use rbenv (or RVM) to install or reinstall Ruby of whichever version your app requires with a prefix like so:

$ RUBY_CONFIGURE_OPTS =-- with - jemalloc rbenv install 2.4 . 1 # As of September, 2018

If ruby is already installed on your machine as it was in our case you might need to purge and remove older shims and rehash it again, like so:

$ RUBY_CONFIGURE_OPTS =-- with - jemalloc rbenv install 2.4 . 1 ➜ rbenv: /home/m arvin / . rbenv / versions / 2.4 . 1 already exists continue with installation? ( y / N ) y Downloading ruby - 2.4 . 1 . tar . bz2 ... -> https :/ / cache . ruby - lang . org / pub / ruby / 2.4 / ruby - 2.4 . 1 . tar . bz2 Installing ruby - 2.4 . 1 ... ruby - build: use readline from homebrew Installed ruby - 2.4 . 1 to /home/m arvin / . rbenv / versions / 2.4 . 1 rbenv: cannot rehash: /home/m arvin / . rbenv / shims / . rbenv - shim exists $ rm /home/m arvin / . rbenv / shims / . rbenv - shim $ rbenv rehash

To check if your installation of Ruby is now using jemalloc, try following command:

$ ruby - r rbconfig - e "puts RbConfig::CONFIG['LIBS']" - lpthread - ljemalloc - lgmp - ldl - lcrypt - lm # Output

Presence of a string like -ljemalloc in the output indicates that the memory allocator library we wanted is correctly loaded before starting ruby. Brilliant! Now let’s go ahead and restart our app, and there it is folks: a faster rails app that’s also light on your wallet.

Like it? Great! Do you have more tips or tricks to suggest for vertical scaling?

Hi! I’m Marvin Danig CEO and Cofounder of Bubblin Superbooks.

P.S.: Did you know that we bring books straight on web with Bubblin Superbooks?