This page mainly describes sl_alloc
release 2 which is
enabled by default. See
System Flags Affecting sl_alloc
on how to enable sl_alloc
release 1.
The main idea behind sl_alloc
is to use one allocator for
short lived memory blocks and another allocator which puts more
effort in finding a good fit (perhaps the best fit) for long lived
memory blocks. The other allocator is expected to do a much better
job with the long lived memory blocks when it is not disturbed by
short lived blocks with less fragmentation in the areas where long
lived blocks are placed as a result. Putting more effort in finding
a good fit for long lived memory blocks than short lived is more
rewarding since it will have a longer effect. By putting less
effort in finding a good fit for short lived memory blocks the CPU
load will decrease. The fragmentation in these areas is expected
to increase, but as long as it is kept on a reasonable level the
overall fragmentation can be reduced at the same time as the CPU
load is reduced.
sl_alloc
manages multiple areas, called carriers, in which
memory blocks are placed. A carrier is either placed in a memory
segment created by a call to the system call mmap()
or in
the heap segment (by a call to malloc()
). Multiblock
carriers are used for storage of several small blocks, and
singleblock carriers are used for storage of one large block.
A "good fit" algorithm is used for management of blocks in multiblock carriers. Segregated free lists with a maximum search depth (in each list) are used in order to find a good fit fast. Boundary tags (headers and footers) in free blocks are used for fast coalescing.
The other allocator (typically malloc()
) will normally
manage the heap segment. By placing sl_alloc
carriers in
memory segments created by mmap()
, sl_alloc
will
disturb the other allocator as little as possible. Really large
requests to sl_alloc
are placed in singleblock carriers
in order to avoid creating really large holes. The downside of
this is an increased number of mmap/munmap
calls which
can give a performance penalty.
sl_alloc
release 1 only manages singleblock carriers,
that is, blocks that are placed in multiblock carriers by
sl_alloc
release 2 are allocated by malloc()
in
release 1.
Mainly Erlang heap data (Erlang heaps, and message buffers) has been classified as short lived, and most of the other data has been classified as long lived. Erlang heap data has been classified as short lived because it is frequently moved between memory blocks due to copying garbage collection.
"Other data" that may consume large amounts of memory is mainly ETS data and large binaries. ETS data and large binary data have been classified as long lived since the data will not be moved until it is removed.
One cannot say that all ETS and binary data are long lived and all Erlang heap data is short lived, but it should be safe to say that most ETS and binary data are more long lived than most Erlang heap data.
The use of sl_alloc
mainly has an advantage when there
is a significant amount of ETS and/or binary data since
malloc
will be able to do a better job with this data
when it does not have to bother with the Erlang heap data.
If almost all data in the system is being stored on the
process heaps, the situation is not as good since the main part
of the memory will be allocated by sl_alloc
which
probably fragments the memory more than malloc
. The
situation is not as bad as it seems though. When the data is
stored on Erlang heaps it will be stored in larger but fewer
blocks than if it had been stored in ETS tables which simplifies
the job for sl_alloc
. sl_alloc
is also quite good
at preventing fragmentation even though malloc
probably
is better. When memory consumption decreases heavily,
sl_alloc
decreases the total amount of used pages far
better than a malloc
implementation only using the heap
segment. sl_alloc
also still has the advantage of
reducing CPU load compared to a malloc
implementation
that is putting more effort in finding a good fit. But in the
case that the memory consumed mainly consist of Erlang heap
data, the malloc
implementation used causes a lot less
fragmentation than sl_alloc
, and there is a memory
shortage, it may be best not to enable sl_alloc
release 2.
Only use these flags if you are absolutely sure what you are doing. Unsuitable settings may cause serious performance degradation (and even a system crash) at any time during operation. |
The following sl_alloc
flags can be passed to the Erlang
virtual machine (see also erl(1)):
sl_alloc
(default true if mmap()
is available;
otherwise, false).
sl_alloc
(default 2). Currently
release 1 (version 1.0) and release 2 (version 1.9.3) can be
enabled.
mmap()
. When this limit has
been reached, new carriers will be placed on the heap segment
(by using malloc()
instead of mmap()
).
sl_realloc()
will be left in an unchanged singleblock carrier if the block
fits, the ratio of unused memory (in percent) is less than
this threshold, and a (copying) move will be avoided;
otherwise, it will be moved into a new singleblock carrier or
into a multiblock carrier.
Most of these flags are highly implementation dependent, and they may be changed or removed without prior notice. The |