On Ruby 2.0 memory usage, Unicorn and Heroku

On UNIX-like operating systems (e.g. Linux) there's the concept of process forking via the fork() system call. When one calls fork from a parent-process it will spawn a new child-process - that is a copy of its parent, then both processes return from fork. The child-process has an exact copy of the parent's memory in a separate address space. Due to this clearly not being efficient, UNIX implements the copy-on-write semantics (i.e. CoW): this delays the actual copying of memory until it needs to be written - simple and elegant.

The old days of Ruby 1.8.7 and REE

Historically, Ruby has been really bad at forking and I'm not referring to the language itself but to its VMs implementations: especially the MRI and the YARV. The issue is the way garbage collection works in these versions, without entering into details, when the GC runs to clear some unused references from memory it changes every object making them "dirty" i.e. CoW fails quickly as the whole memory becomes dirty. Why is this important ? The best Rack compliant servers use pre-forking to max out all CPUs (e.g. Unicorn redirect, Phusion Passenger redirect).

When you fire up a Unicorn server it creates a number of a priori configured workers via the fork() system call. On a 64bit Linux, a Rails 3.x, almost vanilla application takes out ~ 70M of memory and a rather complex one ~ 200M, now multiply that to the number of available cores on your system, let's say eight and we have a memory usage spanning from 560M to a whopping 1.6G. Using Ruby 1.8.7 yields a nice bonus : there's a CoW friendly version with an updated garbage collection algorithm written by the guys at Phusion redirect - in the form of Ruby Enterprise Edition - REE redirect.

This is not a solution as 1.8.7 nears EOL on June 2013 and REE is next redirect, nevertheless REE was/is a great piece of software.

The recent release of Ruby 2.0

Narihiro Nakamura implemented the new GC algorithm - bitmap marking: in a nutshell - the Ruby VM can do its sweeping without actually modifying the objects i.e. CoW works as expected now. For more details you can check out a video redirect about Ruby's GC and a recent really nice high-level explanation redirect by Pat Shaughnessy.

When you combine Ruby 2.0 with Unicorn you can get some pretty impressive results. Using this simple script memstats.rb redirect I checked the memory usage with eight workers:

1
2
3
4
5
6
7
8
9
Memory Summary: private_clean 0 kB private_dirty 1,584 kB pss 12,884 kB rss 80,152 kB shared_clean 1,984 kB shared_dirty 76,584 kB size 275,704 kB swap 0 kB

Based on this gist redirect : “rss represents the physical memory that is actually used, and it's comprised of private_clean + private_dirty + shared_clean + shared_dirty” so practically ~ 76M is shared and ~ 1.5M is private. This is a huge step-up from YARV 1.9.3 where there's no CoW friendly GC i.e. no memory sharing whatsoever. These results are impressive, of course my app is rather trivial (it just runs this blog) so in real ones the ratio will not be as high but still big enough to save lots or ram.

What about Heroku ?

One Heroku Dyno is limited to 512M of ram after which it starts swapping memory until it crashes and/or Heroku will send a SIGKILL and restart your app - more info redirect. Due to the recent routing-miscommunication, we all know that the best server for a Rack compliant app is Unicorn (actually, that should be common knowledge).

1
2
# config/unicorn.rb worker_processes 8

What happens when you upgrade to Ruby 2.0 ? You can crank up the workers as much as doubling them without hitting the hard quota.

Wrap-up

Clearly this does not benefit only Heroku users, the upgrade to Ruby 2.0 is painless as in most cases is a drop-in replacement with the added bonus of lower memory usage. The big issue is with apps that run 1.8.7 or REE as upgrading from 1.8.7 to 1.9.3 and 2.0 yields some compatibility problems.

Related articles

On font rendering consistency across browsers

Back in the day (i.e. when Firefox's version was a single digit) font rendering was at its best on Mozilla's browser. I remember comparing Firefox's anti-aliasing to the new-kid-on-the-block Chrome : the difference was huge.

Time went by and we all know what happened : most web developers found themselves using Chrome as their main browser for doing their work and this is exactly what happened to me. Somehow the font rendering issue went under the radar.

Fast-forward to today : I fired up Firefox to check the rendering of my blog and surprise: faux bold on all my main body text. I've tried the classic font-weight: normal and opacity: 0.99 with no luck.

After some major goggling around I've found there's a special CSS property that addresses this namely : text-stroke. The irony redirect : the article referencing this was trying to replicate the exact opposite i.e. making text render in Webkit browsers in a faux bold style à la Firefox.

Playing a bit with the property in Chrome one can easily duplicate the FF rendering style with text-stroke: 0.35px and by setting it to 0px we practically reset it ergo using the same in FF will reset its rendering. Sadly as you can see in this lovely table by caniuse redirect Firefox and Opera don't support it, well, Opera by going the Webkit way, it will support it soon.

How about Internet Explorer ? Good question indeed ! and since IE10 is out for some time I updated my old version 9 (it needed a restart of course) and fired it up. Results : horrendous text rendering and a very snappy experience.

I'll probably do some testing (all of this was done on Windows) on Linux and OS X soon, there I hope/should get more consistent results.

There's also:

1
2
3
-webkit-font-smoothing: none; -webkit-font-smoothing: antialiased; -webkit-font-smoothing: subpixel-antialiased;

yet it doesn't seem to do much.

It seems that font rendering is not consistent at all under the Windows platform, I reckon some of these problems are also due to the hardware acceleration implementation. I really dislike how Firefox renders fonts via the CSS @font-face property (and yes the Verb typeface is optimized for web).

The issue is clearly visible when using font sizes in the range of 16 ~ 32 pixels. On smaller sizes the effect is actually useful as it makes the text more readable.

Update № 1

There's a simple fix for Firefox : detection is done via JavaScript and it just adds a class to the body element, then we can do :

1
.firefox_is_bad section p { opacity: 0.75; }

That's just a joke I still love Mozilla's browser.

The code for easily detecting Firefox :

1
2
is_firefox: -> if window.mozInnerScreenX is undefined then false else true

Update № 2

Status on OS X : same issue yet the native antialiasing is somewhat better compared to the win platform.

The -webkit-font-smoothing css property seems to work here in Chrome the rendering looks like :

As one can clearly see using -webkit-font-smoothing: antialiased; renders the text best, somehow I was living under the impression that subpixel antialiasing would render better.

Hopefully this will get fixed at some point.

On unnamed abstracts, entities and happiness

When I look upon my day to day behavior the pattern that pops out is that of abstracted sets of ideas interchanging one another or making connection to other entities, bringing them in the foreground, into my field of subjectivity.

We try to to make perception ours thus abstracting it away in nicely grouped entities that can relate to one another. I'm doing this daily e.g when I open my browser and visit a webpage, what I'm actually doing is accessing an abstracted entity from my memory : a browser, a webpage, the Internet.

The issue here is that a subjective quotient comes into play that creates leaky abstractions. Reductionist views in which details dissolve without a trace, one could argue that by themselves abstractions can be viewed as a reductionist view of perception or better yet : semantic compression of streaming information.

We must draw a line between what a “good” abstraction would involve and
a leaky one would dissolve. Seems like we have more questions than facts.
Looking from another angle and replacing the "good" with the standard
definition of utilitarianism :

Utilitarianism is a theory in normative ethics holding that the proper course of action is the one that maximizes utility, specifically defined as maximizing happiness and reducing suffering.

From this perspective a proper abstraction is one that maximizes utility (happiness could be a far product of this). Utility in the sense that while keeping semantic compression close to an ideal ratio our abstraction preserves its most important details; by contrast a leaky one would miss them which in relationship with other entities this would cause wrong interpretations and thus unhappiness.

Getting back to reality: all abstractions are leaky, the important thing is not to miss those capital details that could blur everything up. Our perception is by definition subjective but we also apply a semantic compression to different chunks of it to be able to easily work with them.

This leads to another layer of subjectivity, the key is that we should be eternally vigilant.

Marian's avatar

Marian Posăceanu

rubyist @ okapistudio

ascend