Donato Donato - 6 months ago 11
Ruby Question

Is 'require' preferrable to 'autoload' with Ruby 2 in a multi-process fork server?

I am reading this article. My concern is the benefits of

autoload
vs
require
. From reading the article, what I gather is that using
autoload
for multi-threaded servers is bad because one thread might try to load an object that is not in memory yet.

The article says what about multi-process servers? Is autoload good for those? Then it says it depends. If the server uses fork (which spawns a new process for each request), such as Phusion Passenged, and you are using Ruby 2, then autoload is not beneficial.

The reason is because Ruby 2 uses copy-on-write semantics. This means it is better to use
require
than
autoload
. With copy-on-write semantics, if we load
Foo::Bar
on boot, we will have one copy of
Foo::Bar
shared between all processes. Hence, there will be no big memory footprint.

However, if we are not using Ruby 2 and we are not using a multi-process server that uses fork, each process will end up loading its own copy of
Foo::Bar
possibly leading to higher memory usage. Therefore, in that case
autoload
is preferrable to
require
.

Is my interpretation of the article correct?

Answer

I think you've got it, but it would be good to restate a couple of points to be clear:

  1. The important distinction isn't quite between require and autoload but between eager and lazy loading. Eager loading is thread-safe and memory efficient when forking, but it slows server startup. Lazy loading is neither thread-safe nor memory efficent when forking, but it allows fast server startup. require or autoload together with Rails eager_autoload eager load; autoload by itself lazy loads.

  2. With the above in mind, different servers and Ruby versions raise different issues for lazy and eager loading:

    • In a threading server, lazy loading is unsafe, so eager loading is (ahem) required.
    • In an evented server, lazy loading is fine, so you might as well lazy-load for fast server startup.
    • In a forking server, lazy loading is safe but memory-inefficient.
      • In Ruby < 2, eager loading is also memory-inefficient, since Ruby < 2 doesn't support copy-on-write. So you might as well load lazily. (Actually, what you should do is upgrade to current Ruby.)
      • In Ruby >= 2, eager loading is memory-efficient, since it takes advantage of copy-on-write, and therefore preferred.
Comments