Improving mod_perl Driven Site's Performance -- Part IV: Sharing Memory
Introduction
If your OS supports sharing of memory (and most sane systems do), you might save a lot of RAM by sharing it between child processes. This will allow you to run more processes and hopefully better satisfy the client, without investing extra money into buying more memory.
If your OS supports sharing of memory, you might save a lot of RAM by sharing it between child processes. This will allow you to run more processes and hopefully better satisfy the client, without investing extra money into buying more memory.This is only possible when you preload code at server startup. However, during a child process' life its memory pages tend to become unshared. There is no way we can make Perl allocate memory so that (dynamic) variables land on different memory pages from constants, so the copy-on-write effect will hit you almost at random.
If you are pre-loading many modules you might be able to trade off the
memory that stays shared against the time for an occasional fork by
tuning MaxRequestsPerChild
. Each time a child reaches this upper
limit and dies it should release its unshared pages. The new child
which replaces it will share its fresh pages until it scribbles on
them.
The ideal is a point where your processes usually restart before too
much memory becomes unshared. You should take some measurements to
see if it makes a real difference, and to find the range of reasonable
values. If you have success with this tuning the value of
MaxRequestsPerChild
will probably be peculiar to your situation and
may change with changing circumstances.
It is very important to understand that your goal is not to have
MaxRequestsPerChild
to be 10000. Having a child serving 300
requests on precompiled code is already a huge overall speedup, so if
it is 100 or 10000 it probably does not really matter if you can save
RAM by using a lower value.
Do not forget that if you preload most of your code at server startup, the newly forked child gets ready very very fast, because it inherits most of the preloaded code and the perl interpreter from the parent process.
During the life of the child its memory pages (which aren't really its own to start with, it uses the parent's pages) gradually get 'dirty' - variables which were originally inherited and shared are updated or modified -- and the copy-on-write happens. This reduces the number of shared memory pages, thus increasing the memory requirement. Killing the child and spawning a new one allows the new child to get back to the pristine shared memory of the parent process.
The recommendation is that MaxRequestsPerChild
should not be too
large, otherwise you lose some of the benefit of sharing memory.
How Shared Is My Memory?
You've probably noticed that the word shared is repeated many times in relation to mod_perl. Indeed, shared memory might save you a lot of money, since with sharing in place you can run many more servers than without it.
How much shared memory do you have? You can see it by either using
the memory utility that comes with your system or you can deploy the
GTop
module:
use GTop (); print "Shared memory of the current process: ", GTop->new->proc_mem(20982)->share,"\n";print "Total shared memory: ", GTop->new->mem->share,"\n";When you watch the output of the
top
utility, don't confuse theRES
(orRSS
) columns with theSHARE
column.RES
is RESident memory, which is the size of pages currently swapped in.Calculating Real Memory Usage