Skip to content

Instantly share code, notes, and snippets.

@garazdawi
Created September 6, 2019 14:20
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save garazdawi/17cdb5914b950f0acae21d9fcf7e8d41 to your computer and use it in GitHub Desktop.
Save garazdawi/17cdb5914b950f0acae21d9fcf7e8d41 to your computer and use it in GitHub Desktop.
Global Counters in Erlang
-module(gcount).
-export([bench/0,bench/1]).
-export([ets_init/0,ets_new/1,ets_incr/1,ets_read/1]).
-export([cnt_init/0,cnt_new/1,cnt_incr/1,cnt_read/1]).
-export([cnt_pt_init/0,cnt_pt_new/1,cnt_pt_incr/1,cnt_pt_read/1]).
ets_init() ->
ets:new(?MODULE, [{write_concurrency,true},named_table,public]).
ets_new(Counter) ->
[ets:insert(?MODULE,{{Counter,I},0})
|| I <- lists:seq(1,erlang:system_info(schedulers))].
ets_incr(Counter) ->
ets:update_counter(?MODULE,{Counter,erlang:system_info(scheduler_id)},1).
ets_read(Counter) ->
lists:sum(ets:select(?MODULE,[{{{Counter,'_'},'$1'},[],['$1']}])).
cnt_init() ->
ets:new(?MODULE, [{read_concurrency,true},named_table,public]).
cnt_new(Counter) ->
ets:insert(?MODULE,{Counter,counters:new(1,[write_concurrency])}).
cnt_incr(Counter) ->
counters:add(ets:lookup_element(?MODULE,Counter,2),1,1).
cnt_read(Counter) ->
counters:get(ets:lookup_element(?MODULE,Counter,2),1).
cnt_pt_init() ->
ok.
cnt_pt_new(Counter) ->
persistent_term:put({?MODULE,Counter},counters:new(1,[write_concurrency])).
cnt_pt_incr(Counter) ->
counters:add(persistent_term:get({?MODULE,Counter}),1,1).
cnt_pt_read(Counter) ->
counters:get(persistent_term:get({?MODULE,Counter}),1).
bench() ->
{Ets,EtsGc} = bench(ets),
{Counter,CounterGc} = bench(counters),
{CounterPT,CounterPTGc} = bench(counters_pt),
#{ets => {Ets / Ets * 100, EtsGc / EtsGc * 100, Ets, EtsGc},
counters => {Counter / Ets * 100,CounterGc / EtsGc * 100, Counter, CounterGc},
counters_pt => {CounterPT / Ets * 100,CounterPTGc / EtsGc * 100, CounterPT, CounterPTGc}}.
bench(Type) ->
Types = #{ ets => {fun ets_init/0,fun ets_new/1,fun ets_loop/0,fun ets_read/1},
counters => {fun cnt_init/0,fun cnt_new/1,fun cnt_loop/0,fun cnt_read/1},
counters_pt => {fun cnt_pt_init/0,fun cnt_pt_new/1,fun cnt_pt_loop/0,fun cnt_pt_read/1}
},
{Init, New, Loop, Read} = maps:get(Type, Types),
{_,GCBefore,_} = erlang:statistics(garbage_collection),
spawn_monitor(
fun() ->
erlang:process_flag(priority,high),
Init(),
New(?MODULE),
Pids = [spawn_monitor(Loop) || _ <- lists:seq(1,20)],
[P ! go || {P,_} <- Pids],
timer:sleep(5000),
[exit(P,kill) || {P,_} <- Pids],
[receive _ -> ok end || _ <- Pids],
exit(Read(?MODULE))
end),
receive {_,_,_,_,M} ->
{_,GCAfter,_} = erlang:statistics(garbage_collection),
{M, (GCAfter-GCBefore) / M}
end.
ets_loop() ->
?MODULE:ets_incr(?MODULE),
?MODULE:ets_incr(?MODULE),
?MODULE:ets_incr(?MODULE),
?MODULE:ets_incr(?MODULE),
?MODULE:ets_incr(?MODULE),
?MODULE:ets_incr(?MODULE),
?MODULE:ets_incr(?MODULE),
?MODULE:ets_incr(?MODULE),
?MODULE:ets_incr(?MODULE),
?MODULE:ets_incr(?MODULE),
?MODULE:ets_incr(?MODULE),
ets_loop().
cnt_loop() ->
?MODULE:cnt_incr(?MODULE),
?MODULE:cnt_incr(?MODULE),
?MODULE:cnt_incr(?MODULE),
?MODULE:cnt_incr(?MODULE),
?MODULE:cnt_incr(?MODULE),
?MODULE:cnt_incr(?MODULE),
?MODULE:cnt_incr(?MODULE),
?MODULE:cnt_incr(?MODULE),
?MODULE:cnt_incr(?MODULE),
?MODULE:cnt_incr(?MODULE),
?MODULE:cnt_incr(?MODULE),
cnt_loop().
cnt_pt_loop() ->
?MODULE:cnt_pt_incr(?MODULE),
?MODULE:cnt_pt_incr(?MODULE),
?MODULE:cnt_pt_incr(?MODULE),
?MODULE:cnt_pt_incr(?MODULE),
?MODULE:cnt_pt_incr(?MODULE),
?MODULE:cnt_pt_incr(?MODULE),
?MODULE:cnt_pt_incr(?MODULE),
?MODULE:cnt_pt_incr(?MODULE),
?MODULE:cnt_pt_incr(?MODULE),
?MODULE:cnt_pt_incr(?MODULE),
?MODULE:cnt_pt_incr(?MODULE),
cnt_pt_loop().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment