Last active
August 29, 2015 14:08
-
-
Save sile/85232feebbac43e79955 to your computer and use it in GitHub Desktop.
ErlangでKVS的なプロセスのRead性能をコア数に対してスケールさせる方法 ref: http://qiita.com/sile/items/f3a2d2bea0135847523d
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(bench_parallel). | |
-export([bench/4]). | |
%% @doc ベンチマーク関数 | |
%% | |
%% 一秒間に何回読み込み(検索)処理を行えるかを返す | |
-spec bench(module(), non_neg_integer(), non_neg_integer(), pos_integer()) -> ReadsPerSecond::non_neg_integer(). | |
bench(Module, EntryCount, ReadCount, ClientCount) -> | |
%% KVSプロセスの起動 | |
{ok, Pid} = Module:start_link(), | |
%% 最初に要素を登録しておく | |
ok = lists:foreach( | |
fun (I) -> Module:store(I, I, Pid) end, | |
lists:seq(0, EntryCount - 1)), | |
_ = timer:sleep(10), | |
%% 各クライアントプロセスが担当する読み込み処理の個数を計算する(端数の処理はいい加減) | |
PerProcessReadCount = ReadCount div ClientCount, | |
%% 読み込み性能を測定する | |
{Elapsed, _} = | |
timer:tc( | |
fun () -> | |
%% クライアントプロセスを起動 | |
ok = lists:foreach( | |
fun (_) -> | |
spawn_monitor(bench_serial, find_loop, | |
[PerProcessReadCount, Module, Module, EntryCount]) | |
end, | |
lists:seq(1, ClientCount)), | |
%% 全てのクライアントの処理が終わるまで待機 | |
wait_loop(ClientCount) | |
end), | |
%% KVSプロセスの停止 | |
_ = unlink(Pid), | |
_ = exit(Pid, kill), | |
(ReadCount * 1000 * 1000) div Elapsed. | |
-spec wait_loop(non_neg_integer()) -> ok. | |
wait_loop(0) -> ok; | |
wait_loop(N) -> | |
receive | |
{'DOWN', _, _, _, normal} -> wait_loop(N - 1) | |
end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(bench_serial). | |
-export([bench/3]). | |
-export([find_loop/4]). % 後で参照したいので公開関数にしておく | |
%% @doc ベンチマーク関数 | |
%% | |
%% 一秒間に何回読み込み(検索)処理を行えるかを返す | |
-spec bench(module(), non_neg_integer(), non_neg_integer()) -> ReadsPerSecond::non_neg_integer(). | |
bench(Module, EntryCount, ReadCount) -> | |
%% 最初に要素を登録しておく | |
Map = | |
lists:foldl( | |
fun (I, Acc) -> Module:store(I, I, Acc) end, | |
Module:new(), | |
lists:seq(0, EntryCount - 1)), | |
%% 読み込み性能を測定する | |
{Elapsed, _} = | |
timer:tc( | |
fun () -> | |
find_loop(ReadCount, Module, Map, EntryCount) | |
end), | |
(ReadCount * 1000 * 1000) div Elapsed. | |
%% @doc 要素の検索処理を指定回数分だけ行う | |
-spec find_loop(non_neg_integer(), module(), term(), non_neg_integer()) -> ok. | |
find_loop(0, _, _, _) -> | |
ok; | |
find_loop(Rest, Module, Map, Limit) -> | |
I = Rest rem Limit, | |
{ok, I} = Module:find(I, Map), | |
find_loop(Rest - 1, Module, Map, Limit). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% dictを使ったKVS的サーバプロセス (必要最低限の実装) | |
-module(dict_kvs). | |
-export([start_link/0, store/3, find/2]). | |
-record(state, {map :: dict:dict()}). | |
%%% external functions | |
-spec start_link() -> {ok, pid()}. | |
start_link() -> | |
Pid = spawn_link(fun() -> loop(#state{map = dict:new()}) end), | |
true = register(?MODULE, Pid), % モジュール名 == プロセス名 | |
{ok, Pid}. | |
-spec store(term(), term(), pid()|atom()) -> ok. | |
store(Key, Value, Server) -> | |
_ = Server ! {store, Key, Value}, | |
ok. | |
-spec find(term(), pid()|atom()) -> {ok, Value::term()} | error. | |
find(Key, Server) -> | |
% メッセージパッシングを使って、サーバに値を問い合わせる | |
Ref = make_ref(), | |
_ = Server ! {find, self(), Ref, Key}, | |
receive | |
{Ref, Result} -> Result | |
end. | |
%%% internal functions | |
-spec loop(#state{}) -> no_return(). | |
loop(State) -> | |
receive | |
{store, Key, Value} -> | |
Map = dict:store(Key, Value, State#state.map), | |
loop(State#state{map = Map}); | |
{find, From, Ref, Key} -> | |
_ = From ! {Ref, dict:find(Key, State#state.map)}, | |
loop(State) | |
end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% 名前付きETSを使ったKVS的サーバプロセス (必要最低限の実装) | |
-module(ets_kvs). | |
-export([start_link/0, store/3, find/2]). | |
-record(state, {map :: ets:tid()}). | |
%%% external functions | |
-spec start_link() -> {ok, pid()}. | |
start_link() -> | |
Pid = spawn_link(fun() -> loop(#state{map = ets_map:new(?MODULE)}) end), | |
true = register(?MODULE, Pid), % モジュール名 == プロセス名 | |
{ok, Pid}. | |
-spec store(term(), term(), pid()|atom()) -> ok. | |
store(Key, Value, Server) -> | |
_ = Server ! {store, Key, Value}, | |
ok. | |
-spec find(term(), pid()|atom()) -> {ok, Value::term()} | error. | |
find(Key, _Server) -> | |
%% 検索時にはサーバプロセスに問い合わせることなく、直接ETSテーブルにアクセスする | |
ets_map:find(Key, ?MODULE). | |
%%% internal functions | |
-spec loop(#state{}) -> no_return(). | |
loop(State) -> | |
receive | |
{store, Key, Value} -> | |
Map = ets_map:store(Key, Value, State#state.map), | |
loop(State#state{map = Map}) % dict_kvsに合わせてmapフィールドの更新を行っている(実質的には意味はない) | |
end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(ets_map). | |
-export([new/0, store/3, find/2]). % dictに合わせた公開関数 | |
-export([new/1]). % 名前付きテーブル生成用関数 | |
%% @doc 名前なしテーブルを生成する (単体性能測定用) | |
-spec new() -> ets:tid(). | |
new() -> | |
ets:new(?MODULE, [set, protected, {read_concurrency, true}]). | |
%% @doc 名前付きターブルを生成する (サーバ性能測定用) | |
-spec new(Name::atom()) -> ets:tid(). | |
new(Name) -> | |
ets:new(Name, [named_table, set, protected, {read_concurrency, true}]). | |
-spec store(Key::term(), Value::term(), ets:tid()) -> ets:tid(). | |
store(Key, Value, Map) -> | |
true = ets:insert(Map, {Key, Value}), | |
Map. | |
-spec find(Key::term(), ets:tid()) -> {ok, Value::term()} | error. | |
find(Key, Map) -> | |
case ets:lookup(Map, Key) of | |
[{_, Value}] -> {ok, Value}; | |
[] -> error | |
end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% ets_map | |
> bench_serial:bench(ets_map, 1000, 10000000). % NOTE: 実行が終わっても生成されたETSテーブルは自動では回収されない | |
3377147 % 337万/sec | |
%% dict | |
> bench_serial:bench(dict, 1000, 10000000). | |
2326308 % 232万/sec |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment