Packages

Easily time execution of a block or function!

Current section

Files

Jump to
pocket_watch src pocket_watch@summary.erl
Raw

src/pocket_watch@summary.erl

-module(pocket_watch@summary).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/pocket_watch/summary.gleam").
-export([show_timings/1, show_rates/1, show_overall/1, show_all/1, label/2, collect/3, callback/4, simple/4]).
-export_type([summary/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type summary(EBY) :: {summary,
list(EBY),
list(gleam@time@duration:duration()),
integer(),
integer(),
gleam@time@duration:duration(),
gleam@time@duration:duration(),
gleam@time@duration:duration(),
gleam@time@duration:duration(),
gleam@time@duration:duration(),
gleam@time@duration:duration()}.
-file("src/pocket_watch/summary.gleam", 56).
?DOC(
" Returns a `String` containing the aggregate timing information from a `Summary`.\n"
"\n"
" ## Examples:\n"
" ```gleam\n"
" \"min: 1.0ms, max: 2.0ms, median: 1.5ms, mean: 1.5ms\"\n"
" ```\n"
).
-spec show_timings(summary(any())) -> binary().
show_timings(Summary) ->
<<<<<<<<<<<<<<"min: "/utf8,
(humanise:duration(erlang:element(8, Summary)))/binary>>/binary,
", max: "/utf8>>/binary,
(humanise:duration(erlang:element(9, Summary)))/binary>>/binary,
", median: "/utf8>>/binary,
(humanise:duration(erlang:element(10, Summary)))/binary>>/binary,
", mean: "/utf8>>/binary,
(humanise:duration(erlang:element(11, Summary)))/binary>>.
-file("src/pocket_watch/summary.gleam", 74).
?DOC(
" Returns a `String` containing warmup and post-warmup rates calculated from a `Summary`.\n"
"\n"
" ## Examples:\n"
" ```gleam\n"
" \"warmup: 10.0/s, post-warmup: 30.0/s\"\n"
" ```\n"
).
-spec show_rates(summary(any())) -> binary().
show_rates(Summary) ->
Warmup_runs = erlang:float(erlang:element(5, Summary)),
Warmup_time = gleam@time@duration:to_seconds(erlang:element(6, Summary)),
Post_warmup_runs = erlang:float(erlang:element(4, Summary)),
Post_warmup_time = gleam@time@duration:to_seconds(
erlang:element(7, Summary)
),
Format = fun(N) ->
gleam_stdlib:float_to_string(gleam@float:to_precision(N, 2))
end,
<<<<<<<<"warmup: "/utf8, (Format(case Warmup_time of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Warmup_runs / Gleam@denominator
end))/binary>>/binary, "/s, post-warmup: "/utf8>>/binary, (Format(
case Post_warmup_time of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> Post_warmup_runs / Gleam@denominator@1
end
))/binary>>/binary, "/s"/utf8>>.
-file("src/pocket_watch/summary.gleam", 95).
?DOC(
" Returns a `String` containing the overall information from a `Summary`.\n"
"\n"
" ## Examples:\n"
" ```gleam\n"
" \"runs: 1000, warmup: 200.0ms, total (ex. warmup): 2.0s\"\n"
" ```\n"
).
-spec show_overall(summary(any())) -> binary().
show_overall(Summary) ->
<<<<<<<<<<<<<<"warmup: "/utf8,
(erlang:integer_to_binary(
erlang:element(5, Summary)
))/binary>>/binary,
"/"/utf8>>/binary,
(humanise:duration(erlang:element(6, Summary)))/binary>>/binary,
", total post-warmup: "/utf8>>/binary,
(erlang:integer_to_binary(erlang:element(4, Summary)))/binary>>/binary,
"/"/utf8>>/binary,
(humanise:duration(erlang:element(7, Summary)))/binary>>.
-file("src/pocket_watch/summary.gleam", 46).
?DOC(
" Returns a `String` containing all aggregate information from a `Summary`.\n"
"\n"
" ## Examples:\n"
" ```gleam\n"
" \"min: 1.0ms, max: 2.0ms, median: 1.5ms, mean: 1.5ms, runs: 10_000, warmup: 800.0ms, total (ex. warmup): 15.0s\"\n"
" ```\n"
).
-spec show_all(summary(any())) -> binary().
show_all(Summary) ->
<<<<(show_timings(Summary))/binary, ", "/utf8>>/binary,
(show_overall(Summary))/binary>>.
-file("src/pocket_watch/summary.gleam", 107).
?DOC(" Prepends a simple label to a `Summary(_) -> String` function.\n").
-spec label(binary(), fun((summary(ECH)) -> binary())) -> fun((summary(ECH)) -> binary()).
label(Label, Callback) ->
fun(Summary) ->
<<<<<<"pocket_watch ["/utf8, Label/binary>>/binary, "]: "/utf8>>/binary,
(Callback(Summary))/binary>>
end.
-file("src/pocket_watch/summary.gleam", 133).
?DOC(
" > **Note**: Be aware of side effects when using any of the summary functions.\n"
" > `body` will be called multiple times and any side effects will be triggered each time.\n"
" \n"
" Run a function `warmup` times and discard the return values, then run it again `n` times and return a `Summary`, which contains:\n"
" - Aggregate statistics (min, max, median, mean, etc.)\n"
" - Per-run times\n"
" - Collected return values from each run\n"
"\n"
" ## Examples:\n"
" ```gleam\n"
" let summary = {\n"
" use <- summary.collect(runs: 100, warmup: 0)\n"
" \n"
" string.repeat(\"meow \", 1_000_000)\n"
" }\n"
" \n"
" summary.show_timings(summary) |> io.println_error\n"
" // min: 1.49ms, max: 4.13ms, median: 2.03ms, mean: 2.11ms\n"
" ```\n"
).
-spec collect(integer(), integer(), fun(() -> ECK)) -> summary(ECK).
collect(Runs, Warmup_runs, Body) ->
Warmup_runs@1 = gleam@int:max(Warmup_runs, 0),
Warmup = gleam@int:range(
0,
Warmup_runs@1,
+0.0,
fun(Acc, _) ->
{_, Ns} = pocket_watch_ffi:elapsed(Body),
Acc + Ns
end
),
Warmup@1 = begin
_pipe = erlang:round(Warmup),
gleam@time@duration:nanoseconds(_pipe)
end,
Zero = gleam@time@duration:nanoseconds(0),
gleam@bool:guard(
Runs < 1,
{summary, [], [], 0, Warmup_runs@1, Zero, Zero, Zero, Zero, Zero, Zero},
fun() ->
{Values, Times} = begin
_pipe@1 = gleam@int:range(
0,
Runs,
[],
fun(Acc@1, _) ->
[pocket_watch_ffi:elapsed(Body) | Acc@1]
end
),
_pipe@2 = lists:reverse(_pipe@1),
gleam@list:unzip(_pipe@2)
end,
Times_int = gleam@list:map(Times, fun erlang:round/1),
Times_sorted = gleam@list:sort(Times_int, fun gleam@int:compare/2),
Total_int = gleam@int:sum(Times_int),
Times@1 = gleam@list:map(
Times_int,
fun gleam@time@duration:nanoseconds/1
),
Total = gleam@time@duration:nanoseconds(Total_int),
Min@1 = case begin
_pipe@3 = gleam@list:first(Times_sorted),
gleam@result:map(_pipe@3, fun gleam@time@duration:nanoseconds/1)
end of
{ok, Min} -> Min;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"pocket_watch/summary"/utf8>>,
function => <<"collect"/utf8>>,
line => 163,
value => _assert_fail,
start => 4666,
'end' => 4751,
pattern_start => 4677,
pattern_end => 4684})
end,
Max@1 = case begin
_pipe@4 = gleam@list:last(Times_sorted),
gleam@result:map(_pipe@4, fun gleam@time@duration:nanoseconds/1)
end of
{ok, Max} -> Max;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"pocket_watch/summary"/utf8>>,
function => <<"collect"/utf8>>,
line => 165,
value => _assert_fail@1,
start => 4754,
'end' => 4838,
pattern_start => 4765,
pattern_end => 4772})
end,
Median@2 = gleam@time@duration:nanoseconds(
case gleam@int:is_even(Runs) of
true ->
{A@1, B@1} = case gleam@list:split(
Times_sorted,
(Runs div 2) - 1
) of
{_, [A, B | _]} -> {A, B};
_assert_fail@2 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"pocket_watch/summary"/utf8>>,
function => <<"collect"/utf8>>,
line => 170,
value => _assert_fail@2,
start => 4928,
'end' => 4996,
pattern_start => 4939,
pattern_end => 4955})
end,
(A@1 + B@1) div 2;
false ->
Median@1 = case gleam@list:split(
Times_sorted,
Runs div 2
) of
{_, [Median | _]} -> Median;
_assert_fail@3 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"pocket_watch/summary"/utf8>>,
function => <<"collect"/utf8>>,
line => 174,
value => _assert_fail@3,
start => 5052,
'end' => 5118,
pattern_start => 5063,
pattern_end => 5081})
end,
Median@1
end
),
Mean = gleam@time@duration:nanoseconds(case Runs of
0 -> 0;
Gleam@denominator -> Total_int div Gleam@denominator
end),
{summary,
Values,
Times@1,
Runs,
Warmup_runs@1,
Warmup@1,
Total,
Min@1,
Max@1,
Median@2,
Mean}
end
).
-file("src/pocket_watch/summary.gleam", 210).
?DOC(
" > **Note**: Be aware of side effects when using any of the summary functions.\n"
" > `body` will be called multiple times and any side effects will be triggered each time.\n"
" \n"
" Run a function `warmup` times and discard the return values, then run it again `n` times and use the provided `callback` to log the generated `Summary` to stderr.\n"
"\n"
" This function returns the first value it collects.\n"
" \n"
" ## Examples:\n"
" ```gleam\n"
" {\n"
" use <- summary.callback(runs: 100, warmup: 10, with: summary.show_overall)\n"
" \n"
" string.repeat(\"meow \", 1_000_000)\n"
" }\n"
" // runs: 100, warmup: 30.78ms, total (ex. warmup): 222.19ms\n"
" ```\n"
).
-spec callback(
integer(),
integer(),
fun((summary(ECO)) -> binary()),
fun(() -> ECO)
) -> ECO.
callback(N, Warmup, Callback, Body) ->
Summary = collect(gleam@int:max(N, 1), Warmup, Body),
Return@1 = case gleam@list:first(erlang:element(2, Summary)) of
{ok, Return} -> Return;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"pocket_watch/summary"/utf8>>,
function => <<"callback"/utf8>>,
line => 217,
value => _assert_fail,
start => 6192,
'end' => 6242,
pattern_start => 6203,
pattern_end => 6213})
end,
gleam_stdlib:println_error(Callback(Summary)),
Return@1.
-file("src/pocket_watch/summary.gleam", 241).
?DOC(
" > **Note**: Be aware of side effects when using any of the summary functions.\n"
" > `body` will be called multiple times and any side effects will be triggered each time.\n"
" \n"
" Run a function `warmup` times and discard the return values, then run it again `n` times and log the generated `Summary` to stderr.\n"
"\n"
" This function returns the first value it collects.\n"
" \n"
" ## Examples:\n"
" ```gleam\n"
" {\n"
" use <- summary.simple(label: \"test\", runs: 100, warmup: 0)\n"
" \n"
" string.repeat(\"meow \", 1_000_000)\n"
" }\n"
" // pocket_watch [test]: min: 1.45ms, max: 3.28ms, median: 2.02ms, mean: 2.04ms\n"
" // runs: 100, warmup: 0.0ns, total (ex. warmup): 203.73ms\n"
" ```\n"
).
-spec simple(binary(), integer(), integer(), fun(() -> ECP)) -> ECP.
simple(Label, N, Warmup, Body) ->
With = fun(Summary) ->
Header = <<<<"pocket_watch ["/utf8, Label/binary>>/binary, "]: "/utf8>>,
<<<<<<<<Header/binary, (show_timings(Summary))/binary>>/binary,
"\n"/utf8>>/binary,
(gleam@string:repeat(<<" "/utf8>>, string:length(Header)))/binary>>/binary,
(show_overall(Summary))/binary>>
end,
callback(N, Warmup, With, Body).