Skip to content

Instantly share code, notes, and snippets.

Created May 26, 2015 20:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/47cde5e60a619319053f to your computer and use it in GitHub Desktop.
Save anonymous/47cde5e60a619319053f to your computer and use it in GitHub Desktop.
fsm jitter testbed. Sample usage: test_fsm5:go(1000, 40, 50, 5).
-module(test_fsm5).
-behaviour(gen_fsm).
-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
-export([s_firststate/3, s_firststate/2, s_secondstate/3, s_secondstate/2]).
-export([go/4,report/2]).
-record(state, { last_time, count = 0, tickinterval = 100, pid, jitters = [] }).
-define(MAXCOUNT, 1000). % iters
-define(MINCOUNT, 300). % iters. Will not record jitter until this many timeouts have passed. Crude attempt to give schedulers settle time.
init([TickInterval,NotificationPid]) ->
State = #state{ last_time = get_os_time(), tickinterval = TickInterval, pid = NotificationPid },
{ok, s_firststate, State, TickInterval}.
handle_event(_Event, StateName, StateData) ->
{next_state, StateName, StateData}.
handle_sync_event(_Event, _From, StateName, StateData) ->
{next_state, StateName, StateData}.
handle_info(_Info, StateName, StateData) ->
{next_state, StateName, StateData}.
terminate(_Reason, _StateName, _StateData) ->
ok.
code_change(_OldVsn, StateName, StateData, _Extra) ->
{ok, StateName, StateData}.
s_firststate(timeout, #state{ last_time = LastTime, count = Count , tickinterval = TickInterval, pid = NotificationPid, jitters = Jitters } = StateData) ->
NewTime = get_os_time(),
TimeDiff = NewTime - LastTime,
Jitter = TimeDiff - (TickInterval * 1000), % microseconds
gen_fsm:start_timer(TickInterval, timeout),
case {(Count > ?MINCOUNT), (Count < ?MAXCOUNT)} of
{false, true} ->
{ next_state, s_firststate, StateData#state{ last_time = NewTime, count = Count + 1, pid = NotificationPid, jitters = Jitters } };
{true, true} ->
{ next_state, s_firststate, StateData#state{ last_time = NewTime, count = Count + 1, pid = NotificationPid, jitters = [Jitter | Jitters] } };
_ ->
NotificationPid ! bye,
{ next_state, s_secondstate, StateData }
end;
s_firststate({timeout,_,timeout}, StateData) ->
s_firststate(timeout, StateData);
s_firststate(E, StateData) ->
io:format("got unexpected event ~p~n",[E]),
{next_state, s_firststate, StateData}.
s_firststate(Event, From, StateData) ->
io:format("got unexpected event ~p from ~p~n",[Event, From]),
{next_state, s_firststate, StateData}.
s_secondstate(fill_in_ets, #state{ jitters = Jitters, pid = NotificationPid } = StateData) ->
[ ets:insert(metrics, {jitter, J}) || J <- Jitters ],
NotificationPid ! alldone,
{ stop, normal, StateData };
s_secondstate(_Event, StateData) ->
%io:format("got unexpected event ~p",[Event]),
{next_state, s_secondstate, StateData}.
s_secondstate(_Event, _From, StateData) ->
%io:format("got unexpected event ~p from ~p~n",[Event, From]),
{next_state, s_secondstate, StateData}.
go(NumFSMs, TickFrom, TickTo, TickStep) ->
ets:new(metrics, [public, named_table, duplicate_bag, {write_concurrency, true}]),
go_run(NumFSMs, TickFrom, TickTo, TickStep).
go_run(_NumFSMs, TickFrom, TickTo, _TickStep) when TickTo < TickFrom ->
all_done;
go_run(NumFSMs, TickFrom, TickTo, TickStep) ->
ets:delete(metrics, jitter),
io:format("waiting for ~p FSMs, tickrate ~p~n",[NumFSMs, TickFrom]),
Starts = [gen_fsm:start(test_fsm5, [TickFrom, self()], []) || _X <- lists:seq(1,NumFSMs)],
await(NumFSMs),
[ gen_fsm:send_event(P, fill_in_ets) || {ok, P} <- Starts],
await(NumFSMs),
report(TickFrom,NumFSMs),
go_run(NumFSMs, TickFrom + TickStep, TickTo, TickStep).
get_os_time() ->
{MegaS, S, MicroS} = os:timestamp(),
(MegaS * 1000000 * 1000000 + S * 1000000 + MicroS).
await(0) -> ok;
await(N) ->
receive _ ->
await(N-1)
end.
report(Tick, NumFSMs) ->
X = lists:sort([A || {_, A} <- ets:lookup(metrics,jitter)]),
Avg = lists:sum(X)/length(X),
Max = lists:max(X),
Min = lists:min(X),
Median = lists:nth(round(length(X)/2),X),
NinetyFifth = lists:nth(round(length(X) * 0.95),X),
NinetyNinth = lists:nth(round(length(X) * 0.99),X),
ets:insert(metrics, {result, {NumFSMs, Tick, Avg, Median, Min, Max, NinetyFifth, NinetyNinth}}),
io:format("avg: ~p~n",[lists:sum(X)/length(X)]),
io:format("max: ~p~n",[lists:max(X)]),
io:format("min: ~p~n",[lists:min(X)]),
io:format("median: ~p~n",[lists:nth(round(length(X)/2),X)]),
io:format("95th: ~p~n",[lists:nth(round(length(X)*0.95),X)]),
io:format("99th: ~p~n",[lists:nth(round(length(X)*0.99),X)]).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment