In Erlang, when a process's mailbox growth bigger, it runs slower, why?

Viewed 1853

Here is the example:test_for_gen_server.erl

When a process got 10000 messages in its mailbox, it took 0.043 sec to finish. When the number is 50000, it should take 0.215 sec, but the reality is 2.4 sec, 10 times slower. Why?

Erlang/OTP 18 [erts-7.1] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:true]

Eshell V7.1 (abort with ^G)

1> test_for_gen_server:start_link().

{ok,<0.36.0>}

2> test_for_gen_server:test(10000).

ok

======gen_server: Times:10000 Cost:42863

3> test_for_gen_server:test(10000).

ok

======gen_server: Times:10000 Cost:43096

4> test_for_gen_server:test(10000).

ok

======gen_server: Times:10000 Cost:43223

5> test_for_gen_server:test(50000).

ok

======gen_server: Times:50000 Cost:2504395

6> test_for_gen_server:test(50000).

ok

======gen_server: Times:50000 Cost:2361987

7> test_for_gen_server:test(50000).

ok

======gen_server: Times:50000 Cost:2304715
3 Answers

Adding to 蛋汤饭's highly useful reply:

as of Erlang/OTP 20 one can use process flag message_queue_data = off_heap to avoid GC slowdown on large mailboxes. See the documentation for process_flag(Flag :: message_queue_data, MQD) here. See an example in Erlang standard library:

init(_) ->
    %% The error logger process may receive a huge amount of
    %% messages. Make sure that they are stored off heap to
    %% avoid exessive GCs.
    process_flag(message_queue_data, off_heap),
    {ok, []}.

Alternatively, you can use {message_queue_data, off_heap} option when creating a process with erlang:spawn_opt/2.

However in my experience, the off_heap option does not help much. What helps is a custom flow control scheme: let the sender process check queue length of the receiver process with process_info(ReceiverPid, message_queue_len) and throttle if the queue length exceeds 1000. Check periodically, not on every send to avoid killing performance. See also here.

Related