[erlang-questions] In-process memory sharing doesn’t seem to work

Krzysztof Jurewicz krzysztof.jurewicz@REDACTED
Tue Jun 19 16:30:36 CEST 2018

I have a potentially big, in-memory data structure which I need to maintain in two copies, master-slave. The slave however will be synchronized only periodically (currently once per second) and it may accept ephemeral changes (lost with each synchronization). The changes will be relatively minor (most of the data structure will be preserved intact). For real world context, see the quoted documentation at https://github.com/tendermint/abci/issues/274 . A natural way to handle this problem without bothering about efficiency would be to put the slave state in a separate process and to synchronize it periodically by sending a message from master containing current data dump. However sending messages (and spawning new processes) involves copying data, while making an assignment in the same process should allow to benefit from memory sharing.

There seems to be not much documentation about sharing, but it is described at http://erlang.org/doc/efficiency_guide/processes.html#id70776 (“Loss of sharing”) and there is a discussion at http://erlang.org/pipermail/erlang-questions/2009-September/046436.html . The problem is that memory measurements don’t seem to confirm that sharing actually occurs. For demonstration I’ll use the hog module from http://erlang.org/pipermail/erlang-bugs/2007-November/000488.html .

Erlang/OTP 21 [RELEASE CANDIDATE 2] [erts-10.0] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

Eshell V10.0  (abort with ^G)
1> c(hog).
hog.erl:2: Warning: export_all flag enabled - all functions will be exported
hog.erl:29: Warning: variable 'Tree' is unused
2> MemoryFun = fun() -> round(memory(processes_used) / 1024 / 1024) end.
3> MemoryFun().
4> T = hog:tree(25), ok.
5> erts_debug:size(T).
6> erts_debug:flat_size(T).

So far, so good.

7> MemoryFun().

This already looks suspicious. An interesting fact about erlang:memory/1 is that it looks completely unstable, as running it after a while gives a different result:

8> MemoryFun().

top meanwhile reports 20,3% memory usage of 7843892 KiB total, so it’s more close to the first result.

Interesting things happen also when we force garbage collection:

9> garbage_collect(), MemoryFun().

Looks better, however after a while we get back the bigger result, but two calls more are needed:

10> MemoryFun().
11> MemoryFun().

Let’s now create a tuple which references the same tree multiple times:

12> X = {T, T, T, T}, ok.
eheap_alloc: Cannot allocate 5668310376 bytes of memory (of type "heap").

Crash dump is being written to: erl_crash.dump...done

The generated erl_crash.dump takes 6 gigabytes. It’s interesting that we get “ok”, which suggests that there is sufficient memory for X and Erlang shell tries to allocate the huge number of bytes for something else.

Also output of hog:main/0 is completely different than one in the original report:

1> hog:main().
Creating tree:: allocated 0 bytes, heap size 376 bytes
Receiving tree:: allocated 0 bytes, heap size 233 bytes

Does it mean that memory sharing no longer works in Erlang? Or maybe it is a shell-related problem?

More information about the erlang-questions mailing list