Created
September 2, 2018 17:13
-
-
Save potatosalad/985b8dfabf0563a343e158635562a866 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- | |
%% vim: ts=4 sw=4 ft=erlang noet | |
-module(quickbench). | |
%% API | |
-export([bench/2]). | |
-export([bench/3]). | |
-export([compare/3]). | |
%% Records | |
-record(stat, { | |
acc = 0 :: non_neg_integer(), | |
min = 0 :: non_neg_integer(), | |
max = 0 :: non_neg_integer() | |
}). | |
%% Types | |
-type arguments_list(Type) :: [Type]. | |
-type arguments_function(Type) :: | |
fun(() -> arguments_list(Type)). | |
-type arguments(Type) :: arguments_function(Type) | arguments_list(Type). | |
-type arguments() :: arguments(term()). | |
-export_type([arguments/1]). | |
-export_type([arguments/0]). | |
-type metric() :: #{ | |
acc := non_neg_integer(), | |
avg := float(), | |
min := non_neg_integer(), | |
max := non_neg_integer() | |
}. | |
-export_type([metric/0]). | |
-type stats() :: #{ | |
reds := metric(), | |
time := metric() | |
}. | |
-export_type([stats/0]). | |
%%==================================================================== | |
%% API | |
%%==================================================================== | |
-spec bench(function(), arguments()) -> stats(). | |
bench(Function, Arguments) -> | |
bench(Function, Arguments, 1). | |
-spec bench(function(), arguments(), non_neg_integer()) -> stats(). | |
bench(Function, Arguments, N) | |
when is_function(Function) | |
andalso (is_list(Arguments) orelse is_function(Arguments, 0)) | |
andalso (is_integer(N) andalso N > 0) -> | |
{Time, Reds} = bench_loop(N, erlang:self(), #stat{}, #stat{}, Function, Arguments), | |
#{ | |
time => stat_final(Time, N), | |
reds => stat_final(Reds, N) | |
}. | |
-spec compare([{atom(), function()}], arguments(), non_neg_integer()) -> [{atom(), stats()}]. | |
compare(Groups, Arguments, N) | |
when is_list(Groups) | |
andalso (is_list(Arguments) orelse is_function(Arguments, 0)) | |
andalso (is_integer(N) andalso N > 0) -> | |
ResolvedArguments = resolve(Arguments), | |
[begin | |
{Label, bench(Function, ResolvedArguments, N)} | |
end || {Label, Function} <- Groups, is_atom(Label) andalso is_function(Function)]. | |
%%%------------------------------------------------------------------- | |
%%% Internal functions | |
%%%------------------------------------------------------------------- | |
%% @private | |
bench_loop(0, _Self, Time, Reds, _Function, _Arguments) -> | |
{Time, Reds}; | |
bench_loop(I, Self, Time0, Reds0, Function, Arguments) -> | |
Args = resolve(Arguments), | |
T1 = erlang:monotonic_time(), | |
{reductions, R1} = erlang:process_info(Self, reductions), | |
_ = erlang:apply(Function, Args), | |
{reductions, R2} = erlang:process_info(Self, reductions), | |
T2 = erlang:monotonic_time(), | |
Time1 = stat_update(Time0, T2 - T1), | |
Reds1 = stat_update(Reds0, R2 - R1), | |
bench_loop(I - 1, Self, Time1, Reds1, Function, Arguments). | |
%% @private | |
resolve(Arguments) when is_function(Arguments, 0) -> | |
resolve(Arguments()); | |
resolve(Arguments) when is_list(Arguments) -> | |
Arguments. | |
%% @private | |
stat_final(#stat{ acc = Acc, min = Min, max = Max }, N) -> | |
#{ | |
acc => Acc, | |
avg => (Acc / N), | |
min => Min, | |
max => Max | |
}. | |
%% @private | |
stat_update(Stat = #stat{ acc = Acc, min = Min, max = Max }, Val) -> | |
Stat#stat{ | |
acc = Acc + Val, | |
min = | |
case Val < Min of | |
_ when Min =:= 0 -> | |
Val; | |
true -> | |
Val; | |
false -> | |
Min | |
end, | |
max = | |
case Val > Max of | |
true -> | |
Val; | |
false -> | |
Max | |
end | |
}. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment