-module(lightspeed@ops@performance_harness).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/lightspeed/ops/performance_harness.gleam").
-export([pinned_profile/0, default_suite/0, default_budget/0, run_suite_with/2, run_suite/0, evaluate_budget/2, budget_failures/1, workload_label/1, budget_label/1, report_signature/1]).
-export_type([workload/0, profile/0, scenario/0, scenario_report/0, report/0, budget/0, budget_result/0, sample/0, crud_state/0, stream_state/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(" Deterministic benchmark harness for M11 performance parity gate.\n").
-type workload() :: counter | crud_table | stream_heavy_list | form_heavy_flow.
-type profile() :: {profile, binary(), integer(), integer(), integer()}.
-type scenario() :: {scenario, workload(), integer(), integer()}.
-type scenario_report() :: {scenario_report,
workload(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer()}.
-type report() :: {report, profile(), list(scenario_report())}.
-type budget() :: {budget,
workload(),
integer(),
integer(),
integer(),
integer()}.
-type budget_result() :: {budget_result, workload(), boolean(), binary()}.
-type sample() :: {sample, integer(), integer(), integer(), integer()}.
-type crud_state() :: {crud_state, integer(), lightspeed@stream:stream()}.
-type stream_state() :: {stream_state, integer(), lightspeed@stream:stream()}.
-file("src/lightspeed/ops/performance_harness.gleam", 84).
?DOC(" Deterministic pinned profile for M11 reproducibility.\n").
-spec pinned_profile() -> profile().
pinned_profile() ->
{profile, <<"beam-sim-pinned-v1"/utf8>>, 100, 100, 100}.
-file("src/lightspeed/ops/performance_harness.gleam", 95).
?DOC(
" Default benchmark suite for M11:\n"
" counter, CRUD table, stream-heavy list, and form-heavy flow.\n"
).
-spec default_suite() -> list(scenario()).
default_suite() ->
[{scenario, counter, 64, 48},
{scenario, crud_table, 24, 60},
{scenario, stream_heavy_list, 20, 72},
{scenario, form_heavy_flow, 40, 40}].
-file("src/lightspeed/ops/performance_harness.gleam", 109).
?DOC(" Default parity budget for M11 gate checks.\n").
-spec default_budget() -> list(budget()).
default_budget() ->
[{budget, counter, 80, 400, 40, 400},
{budget, crud_table, 120, 1200, 20, 1200},
{budget, stream_heavy_list, 180, 1400, 16, 1800},
{budget, form_heavy_flow, 100, 500, 25, 700}].
-file("src/lightspeed/ops/performance_harness.gleam", 919).
-spec nth(list(integer()), integer()) -> integer().
nth(Values, Index) ->
case Values of
[] ->
0;
[Head | Rest] ->
case Index =< 0 of
true ->
Head;
false ->
nth(Rest, Index - 1)
end
end.
-file("src/lightspeed/ops/performance_harness.gleam", 999).
-spec clamp(integer(), integer(), integer()) -> integer().
clamp(Minimum, Maximum, Value) ->
case Value < Minimum of
true ->
Minimum;
false ->
case Value > Maximum of
true ->
Maximum;
false ->
Value
end
end.
-file("src/lightspeed/ops/performance_harness.gleam", 989).
-spec ceil_div(integer(), integer()) -> integer().
ceil_div(Numerator, Denominator) ->
case Denominator =< 0 of
true ->
0;
false ->
Adjusted = (Numerator + Denominator) - 1,
case Denominator of
0 -> 0;
Gleam@denominator -> Adjusted div Gleam@denominator
end
end.
-file("src/lightspeed/ops/performance_harness.gleam", 908).
-spec insert_sorted(list(integer()), integer()) -> list(integer()).
insert_sorted(Values, Value) ->
case Values of
[] ->
[Value];
[Head | Rest] ->
case Value =< Head of
true ->
[Value, Head | Rest];
false ->
[Head | insert_sorted(Rest, Value)]
end
end.
-file("src/lightspeed/ops/performance_harness.gleam", 901).
-spec sort_ints(list(integer()), list(integer())) -> list(integer()).
sort_ints(Values, Sorted) ->
case Values of
[] ->
Sorted;
[Value | Rest] ->
sort_ints(Rest, insert_sorted(Sorted, Value))
end.
-file("src/lightspeed/ops/performance_harness.gleam", 889).
-spec percentile(list(integer()), integer()) -> integer().
percentile(Values, Pct) ->
case sort_ints(Values, []) of
[] ->
0;
Sorted ->
N = erlang:length(Sorted),
Rank = ceil_div(N * Pct, 100),
Index = clamp(0, N - 1, Rank - 1),
nth(Sorted, Index)
end.
-file("src/lightspeed/ops/performance_harness.gleam", 1017).
-spec max_int(integer(), integer()) -> integer().
max_int(Left, Right) ->
case Left >= Right of
true ->
Left;
false ->
Right
end.
-file("src/lightspeed/ops/performance_harness.gleam", 982).
-spec divide_safe(integer(), integer()) -> integer().
divide_safe(Numerator, Denominator) ->
case Denominator =< 0 of
true ->
0;
false ->
case Denominator of
0 -> 0;
Gleam@denominator -> Numerator div Gleam@denominator
end
end.
-file("src/lightspeed/ops/performance_harness.gleam", 951).
-spec peak_memory_units(list(sample()), integer()) -> integer().
peak_memory_units(Samples, Peak) ->
case Samples of
[] ->
Peak;
[Sample | Rest] ->
peak_memory_units(Rest, max_int(Peak, erlang:element(5, Sample)))
end.
-file("src/lightspeed/ops/performance_harness.gleam", 944).
-spec sum_patch_ops(list(sample()), integer()) -> integer().
sum_patch_ops(Samples, Total) ->
case Samples of
[] ->
Total;
[Sample | Rest] ->
sum_patch_ops(Rest, Total + erlang:element(4, Sample))
end.
-file("src/lightspeed/ops/performance_harness.gleam", 937).
-spec sum_payload_bytes(list(sample()), integer()) -> integer().
sum_payload_bytes(Samples, Total) ->
case Samples of
[] ->
Total;
[Sample | Rest] ->
sum_payload_bytes(Rest, Total + erlang:element(3, Sample))
end.
-file("src/lightspeed/ops/performance_harness.gleam", 930).
-spec sum_latencies(list(sample()), integer()) -> integer().
sum_latencies(Samples, Total) ->
case Samples of
[] ->
Total;
[Sample | Rest] ->
sum_latencies(Rest, Total + erlang:element(2, Sample))
end.
-file("src/lightspeed/ops/performance_harness.gleam", 882).
-spec latencies(list(sample()), list(integer())) -> list(integer()).
latencies(Samples, Latencies_rev) ->
case Samples of
[] ->
lists:reverse(Latencies_rev);
[Sample | Rest] ->
latencies(Rest, [erlang:element(2, Sample) | Latencies_rev])
end.
-file("src/lightspeed/ops/performance_harness.gleam", 775).
-spec latency_ms(
profile(),
workload(),
integer(),
integer(),
integer(),
integer()
) -> integer().
latency_ms(
Profile,
Workload,
Payload_bytes,
Patch_ops,
Memory_units,
Complexity_hint
) ->
Base = case Workload of
counter ->
2;
crud_table ->
5;
stream_heavy_list ->
6;
form_heavy_flow ->
4
end,
Payload_cost = divide_safe(
Payload_bytes,
max_int(18, erlang:element(4, Profile) div 5)
),
Patch_cost = Patch_ops * 2,
Memory_cost = divide_safe(
Memory_units,
max_int(20, erlang:element(5, Profile) div 4)
),
Complexity_cost = Complexity_hint rem 3,
Cpu_discount = divide_safe(erlang:element(3, Profile), 250),
max_int(
1,
((((Base + Payload_cost) + Patch_cost) + Memory_cost) + Complexity_cost)
- Cpu_discount
).
-file("src/lightspeed/ops/performance_harness.gleam", 748).
-spec sample_from_patches(
profile(),
workload(),
list(lightspeed@diff:patch()),
integer(),
integer()
) -> sample().
sample_from_patches(Profile, Workload, Patches, Memory_units, Complexity_hint) ->
Patch_ops = erlang:length(Patches),
Payload_bytes = case Patch_ops =:= 0 of
true ->
0;
false ->
string:length(lightspeed@diff:encode_stream(Patches))
end,
{sample,
latency_ms(
Profile,
Workload,
Payload_bytes,
Patch_ops,
Memory_units,
Complexity_hint
),
Payload_bytes,
Patch_ops,
Memory_units}.
-file("src/lightspeed/ops/performance_harness.gleam", 694).
-spec pending_count_loop(
lightspeed@form_compat:runtime(),
list(binary()),
integer()
) -> integer().
pending_count_loop(Runtime, Names, Count) ->
case Names of
[] ->
Count;
[Name | Rest] ->
case lightspeed@form_compat:has_pending(Runtime, Name) of
true ->
pending_count_loop(Runtime, Rest, Count + 1);
false ->
pending_count_loop(Runtime, Rest, Count)
end
end.
-file("src/lightspeed/ops/performance_harness.gleam", 689).
-spec pending_count(lightspeed@form_compat:runtime()) -> integer().
pending_count(Runtime) ->
Names = [<<"title"/utf8>>,
<<"search"/utf8>>,
<<"status"/utf8>>,
<<"priority"/utf8>>],
pending_count_loop(Runtime, Names, 0).
-file("src/lightspeed/ops/performance_harness.gleam", 671).
-spec form_patch(binary(), binary(), lightspeed@form:form_data(), integer()) -> lightspeed@diff:patch().
form_patch(Kind, Input, Payload, Step) ->
{update_segments,
<<"#form"/utf8>>,
<<"form-flow/v1"/utf8>>,
[lightspeed@diff:slot(<<"kind"/utf8>>, Kind),
lightspeed@diff:slot(<<"input"/utf8>>, Input),
lightspeed@diff:slot(
<<"fields"/utf8>>,
erlang:integer_to_binary(
erlang:length(lightspeed@form:fields(Payload))
)
),
lightspeed@diff:slot(
<<"step"/utf8>>,
erlang:integer_to_binary(Step)
)]}.
-file("src/lightspeed/ops/performance_harness.gleam", 647).
-spec patches_from_dispatches(
list(lightspeed@form_compat:dispatch()),
integer(),
list(lightspeed@diff:patch())
) -> list(lightspeed@diff:patch()).
patches_from_dispatches(Dispatches, Step, Patches_rev) ->
case Dispatches of
[] ->
lists:reverse(Patches_rev);
[Dispatch | Rest] ->
case Dispatch of
{dispatch_change, Input, Payload} ->
patches_from_dispatches(
Rest,
Step,
[form_patch(<<"change"/utf8>>, Input, Payload, Step) |
Patches_rev]
);
{dispatch_submit, Payload@1} ->
patches_from_dispatches(
Rest,
Step,
[form_patch(
<<"submit"/utf8>>,
<<"form"/utf8>>,
Payload@1,
Step
) |
Patches_rev]
);
_ ->
patches_from_dispatches(Rest, Step, Patches_rev)
end
end.
-file("src/lightspeed/ops/performance_harness.gleam", 586).
-spec form_step(
lightspeed@form_compat:runtime(),
integer(),
integer(),
integer()
) -> {lightspeed@form_compat:runtime(), list(lightspeed@form_compat:dispatch())}.
form_step(Runtime, Session_index, Step, At_ms) ->
case Step rem 6 of
1 ->
{Next, Dispatch} = lightspeed@form_compat:change(
Runtime,
<<"title"/utf8>>,
<<<<<<"title=task+"/utf8,
(erlang:integer_to_binary(Session_index))/binary>>/binary,
"-"/utf8>>/binary,
(erlang:integer_to_binary(Step))/binary>>,
At_ms
),
{Next, [Dispatch]};
2 ->
{Next@1, Dispatch@1} = lightspeed@form_compat:change(
Runtime,
<<"search"/utf8>>,
<<"search=query+"/utf8,
(erlang:integer_to_binary(Step))/binary>>,
At_ms
),
{Next@1, [Dispatch@1]};
3 ->
{Next@2, Dispatch@2} = lightspeed@form_compat:change(
Runtime,
<<"priority"/utf8>>,
<<"priority="/utf8,
(erlang:integer_to_binary(Step rem 4))/binary>>,
At_ms
),
{Next@2, [Dispatch@2]};
4 ->
{Next@3, Dispatch@3} = lightspeed@form_compat:blur(
Runtime,
<<"status"/utf8>>,
At_ms
),
{Next@3, [Dispatch@3]};
5 ->
{Next@4, Dispatches} = lightspeed@form_compat:tick(
Runtime,
At_ms + 100
),
{Next@4, Dispatches};
_ ->
{Next@5, Dispatch@4} = lightspeed@form_compat:submit(
Runtime,
<<"title=save&status=open"/utf8>>,
At_ms
),
{Next@5, [Dispatch@4]}
end.
-file("src/lightspeed/ops/performance_harness.gleam", 557).
-spec form_loop(
profile(),
lightspeed@form_compat:runtime(),
integer(),
integer(),
integer(),
list(sample())
) -> list(sample()).
form_loop(Profile, Runtime, Remaining, Session_index, Step, Samples_rev) ->
case Remaining =< 0 of
true ->
lists:reverse(Samples_rev);
false ->
Now_ms = Step * 17,
{Next_runtime, Dispatches} = form_step(
Runtime,
Session_index,
Step,
Now_ms
),
Patches = patches_from_dispatches(Dispatches, Step, []),
Memory = pending_count(Next_runtime) + erlang:length(
lightspeed@form:fields(
lightspeed@form:parse_payload(
<<"title=one&status=open"/utf8>>
)
)
),
Sample = sample_from_patches(
Profile,
form_heavy_flow,
Patches,
Memory,
Step
),
form_loop(
Profile,
Next_runtime,
Remaining - 1,
Session_index,
Step + 1,
[Sample | Samples_rev]
)
end.
-file("src/lightspeed/ops/performance_harness.gleam", 535).
-spec form_heavy_workload(profile(), integer(), integer()) -> list(sample()).
form_heavy_workload(Profile, Events_per_session, Session_index) ->
Runtime = lightspeed@form_compat:new(
[{input_config, <<"title"/utf8>>, immediate},
{input_config, <<"search"/utf8>>, {debounce, 80}},
{input_config, <<"status"/utf8>>, debounce_blur},
{input_config, <<"priority"/utf8>>, {throttle, 60}}]
),
form_loop(Profile, Runtime, Events_per_session, Session_index, 1, []).
-file("src/lightspeed/ops/performance_harness.gleam", 515).
-spec stream_batch(integer(), integer(), list(lightspeed@stream:stream_item())) -> list(lightspeed@stream:stream_item()).
stream_batch(Next_id, Count, Entries_rev) ->
case Count =< 0 of
true ->
lists:reverse(Entries_rev);
false ->
Id = <<"item-"/utf8, (erlang:integer_to_binary(Next_id))/binary>>,
stream_batch(
Next_id + 1,
Count - 1,
[lightspeed@stream:item(
Id,
<<<<<<<<"<li data-id=\""/utf8, Id/binary>>/binary,
"\">B"/utf8>>/binary,
(erlang:integer_to_binary(Next_id))/binary>>/binary,
"</li>"/utf8>>
) |
Entries_rev]
)
end.
-file("src/lightspeed/ops/performance_harness.gleam", 462).
-spec stream_step(stream_state(), integer()) -> {stream_state(),
list(lightspeed@diff:patch()),
integer()}.
stream_step(State, Step) ->
Current_entries = lightspeed@stream:entries(erlang:element(3, State)),
case (Step rem 10) =:= 0 of
true ->
Batch = stream_batch(erlang:element(2, State), 8, []),
{Rows, Patches} = lightspeed@stream:stream(
erlang:element(3, State),
Batch,
begin
_pipe = lightspeed@stream:options(),
_pipe@1 = lightspeed@stream:with_reset(_pipe, true),
lightspeed@stream:with_limit(_pipe@1, -120)
end
),
Memory = (erlang:length(lightspeed@stream:entries(Rows)) * 4) + (erlang:length(
Patches
)
* 2),
{{stream_state, erlang:element(2, State) + 8, Rows},
Patches,
Memory};
false ->
case {(Step rem 3) =:= 0, Current_entries} of
{true, [Head | _]} ->
{Rows@1, Patches@1} = lightspeed@stream:stream_delete(
erlang:element(3, State),
erlang:element(2, Head)
),
Memory@1 = (erlang:length(lightspeed@stream:entries(Rows@1))
* 4)
+ (erlang:length(Patches@1) * 2),
{{stream_state, erlang:element(2, State), Rows@1},
Patches@1,
Memory@1};
{_, _} ->
At = case (Step rem 2) =:= 0 of
true ->
0;
false ->
-1
end,
Id = <<"item-"/utf8,
(erlang:integer_to_binary(erlang:element(2, State)))/binary>>,
Entry = lightspeed@stream:item(
Id,
<<<<<<<<"<li data-id=\""/utf8, Id/binary>>/binary,
"\">"/utf8>>/binary,
(erlang:integer_to_binary(Step))/binary>>/binary,
"</li>"/utf8>>
),
{Rows@2, Patches@2} = lightspeed@stream:stream_insert(
erlang:element(3, State),
Entry,
At,
{some, -120}
),
Memory@2 = (erlang:length(lightspeed@stream:entries(Rows@2))
* 4)
+ (erlang:length(Patches@2) * 2),
{{stream_state, erlang:element(2, State) + 1, Rows@2},
Patches@2,
Memory@2}
end
end.
-file("src/lightspeed/ops/performance_harness.gleam", 435).
-spec stream_loop(
profile(),
stream_state(),
integer(),
integer(),
list(sample())
) -> list(sample()).
stream_loop(Profile, State, Remaining, Step, Samples_rev) ->
case Remaining =< 0 of
true ->
lists:reverse(Samples_rev);
false ->
{Next_state, Patches, Memory_units} = stream_step(State, Step),
Sample = sample_from_patches(
Profile,
stream_heavy_list,
Patches,
Memory_units,
Step + 2
),
stream_loop(
Profile,
Next_state,
Remaining - 1,
Step + 1,
[Sample | Samples_rev]
)
end.
-file("src/lightspeed/ops/performance_harness.gleam", 422).
-spec stream_heavy_workload(profile(), integer(), integer()) -> list(sample()).
stream_heavy_workload(Profile, Events_per_session, Session_index) ->
Seed = {stream_state,
1,
lightspeed@stream:new(
<<"#stream-"/utf8,
(erlang:integer_to_binary(Session_index))/binary>>
)},
stream_loop(Profile, Seed, Events_per_session, 1, []).
-file("src/lightspeed/ops/performance_harness.gleam", 391).
-spec crud_step(crud_state(), integer()) -> {crud_state(),
list(lightspeed@diff:patch()),
integer()}.
crud_step(State, Step) ->
Limit = {some, -60},
case (Step rem 7) =:= 0 of
true ->
Delete_id = <<"row-"/utf8,
(erlang:integer_to_binary(
max_int(1, erlang:element(2, State) - 9)
))/binary>>,
{Rows, Patches} = lightspeed@stream:stream_delete(
erlang:element(3, State),
Delete_id
),
Memory = (erlang:length(lightspeed@stream:entries(Rows)) * 3) + 16,
{{crud_state, erlang:element(2, State), Rows}, Patches, Memory};
false ->
Id = <<"row-"/utf8,
(erlang:integer_to_binary(erlang:element(2, State)))/binary>>,
Row = lightspeed@stream:item(
Id,
<<<<<<<<"<tr><td>"/utf8, Id/binary>>/binary,
"</td><td>Status "/utf8>>/binary,
(erlang:integer_to_binary(Step rem 5))/binary>>/binary,
"</td></tr>"/utf8>>
),
{Rows@1, Patches@1} = lightspeed@stream:stream_insert(
erlang:element(3, State),
Row,
-1,
Limit
),
Memory@1 = (erlang:length(lightspeed@stream:entries(Rows@1)) * 3) + 16,
{{crud_state, erlang:element(2, State) + 1, Rows@1},
Patches@1,
Memory@1}
end.
-file("src/lightspeed/ops/performance_harness.gleam", 370).
-spec crud_loop(profile(), crud_state(), integer(), integer(), list(sample())) -> list(sample()).
crud_loop(Profile, State, Remaining, Step, Samples_rev) ->
case Remaining =< 0 of
true ->
lists:reverse(Samples_rev);
false ->
{Next_state, Patches, Memory_units} = crud_step(State, Step),
Sample = sample_from_patches(
Profile,
crud_table,
Patches,
Memory_units,
Step
),
crud_loop(
Profile,
Next_state,
Remaining - 1,
Step + 1,
[Sample | Samples_rev]
)
end.
-file("src/lightspeed/ops/performance_harness.gleam", 357).
-spec crud_workload(profile(), integer(), integer()) -> list(sample()).
crud_workload(Profile, Events_per_session, Session_index) ->
Seed = {crud_state,
1,
lightspeed@stream:new(
<<"#crud-"/utf8, (erlang:integer_to_binary(Session_index))/binary>>
)},
crud_loop(Profile, Seed, Events_per_session, 1, []).
-file("src/lightspeed/ops/performance_harness.gleam", 967).
-spec send(
lightspeed@agent@session:session(),
binary(),
lightspeed@agent@session:inbox_event()
) -> lightspeed@agent@session:session().
send(State, Owner, Event) ->
lightspeed@agent@session:handle(State, {inbox_message, Owner, Event}).
-file("src/lightspeed/ops/performance_harness.gleam", 959).
-spec ack_first_pending(lightspeed@agent@session:session(), binary()) -> lightspeed@agent@session:session().
ack_first_pending(State, Owner) ->
case lightspeed@agent@session:oldest_pending_patch(State) of
none ->
State;
{some, Patch} ->
send(State, Owner, {ack, lightspeed@agent@session:patch_ref(Patch)})
end.
-file("src/lightspeed/ops/performance_harness.gleam", 709).
-spec sample_from_session_patch(
profile(),
workload(),
lightspeed@agent@session:session()
) -> sample().
sample_from_session_patch(Profile, Workload, State) ->
case lightspeed@agent@session:oldest_pending_patch(State) of
none ->
{sample, latency_ms(Profile, Workload, 0, 0, 0, 1), 0, 0, 0};
{some, Patch} ->
Encoded = lightspeed@diff:encode(
lightspeed@agent@session:patch(Patch)
),
Payload_bytes = string:length(Encoded),
Patch_ops = 1,
Memory_units = lightspeed@agent@session:pending_patch_count(State) + divide_safe(
lightspeed@agent@session:telemetry_count(State),
2
),
{sample,
latency_ms(
Profile,
Workload,
Payload_bytes,
Patch_ops,
Memory_units,
lightspeed@agent@session:counter(State)
),
Payload_bytes,
Patch_ops,
Memory_units}
end.
-file("src/lightspeed/ops/performance_harness.gleam", 335).
-spec counter_loop(
profile(),
lightspeed@agent@session:session(),
binary(),
integer(),
integer(),
list(sample())
) -> list(sample()).
counter_loop(Profile, State, Owner, Remaining, Step, Samples_rev) ->
case Remaining =< 0 of
true ->
lists:reverse(Samples_rev);
false ->
Updated = send(State, Owner, increment),
Sample = sample_from_session_patch(Profile, counter, Updated),
Next = ack_first_pending(Updated, Owner),
counter_loop(
Profile,
Next,
Owner,
Remaining - 1,
Step + 1,
[Sample | Samples_rev]
)
end.
-file("src/lightspeed/ops/performance_harness.gleam", 311).
-spec counter_workload(profile(), integer(), integer()) -> list(sample()).
counter_workload(Profile, Events_per_session, Session_index) ->
Session_id = <<"bench-counter-"/utf8,
(erlang:integer_to_binary(Session_index))/binary>>,
Owner = <<"proc-counter-"/utf8,
(erlang:integer_to_binary(Session_index))/binary>>,
Route = <<"/counter/"/utf8,
(erlang:integer_to_binary(Session_index))/binary>>,
State = lightspeed@agent@session:start(Session_id, Owner, rehydrate, 0, 120),
Connected = send(
State,
Owner,
{connect, Route, <<"csrf-"/utf8, Session_id/binary>>, 0}
),
Connected@1 = ack_first_pending(Connected, Owner),
counter_loop(Profile, Connected@1, Owner, Events_per_session, 1, []).
-file("src/lightspeed/ops/performance_harness.gleam", 295).
-spec run_workload(profile(), workload(), integer(), integer()) -> list(sample()).
run_workload(Profile, Workload, Events_per_session, Session_index) ->
case Workload of
counter ->
counter_workload(Profile, Events_per_session, Session_index);
crud_table ->
crud_workload(Profile, Events_per_session, Session_index);
stream_heavy_list ->
stream_heavy_workload(Profile, Events_per_session, Session_index);
form_heavy_flow ->
form_heavy_workload(Profile, Events_per_session, Session_index)
end.
-file("src/lightspeed/ops/performance_harness.gleam", 975).
-spec prepend_samples(list(sample()), list(sample())) -> list(sample()).
prepend_samples(Values, Acc) ->
case Values of
[] ->
Acc;
[Value | Rest] ->
prepend_samples(Rest, [Value | Acc])
end.
-file("src/lightspeed/ops/performance_harness.gleam", 270).
-spec run_sessions(
profile(),
workload(),
integer(),
integer(),
integer(),
list(sample())
) -> list(sample()).
run_sessions(
Profile,
Workload,
Session_count,
Events_per_session,
Index,
Samples_rev
) ->
case Index > Session_count of
true ->
lists:reverse(Samples_rev);
false ->
run_sessions(
Profile,
Workload,
Session_count,
Events_per_session,
Index + 1,
prepend_samples(
run_workload(Profile, Workload, Events_per_session, Index),
Samples_rev
)
)
end.
-file("src/lightspeed/ops/performance_harness.gleam", 1010).
-spec clamp_positive(integer(), integer()) -> integer().
clamp_positive(Value, Fallback) ->
case Value =< 0 of
true ->
Fallback;
false ->
Value
end.
-file("src/lightspeed/ops/performance_harness.gleam", 237).
-spec run_scenario(profile(), scenario()) -> scenario_report().
run_scenario(Profile, Scenario) ->
Sessions = clamp_positive(erlang:element(3, Scenario), 1),
Per_session = clamp_positive(erlang:element(4, Scenario), 1),
Samples = run_sessions(
Profile,
erlang:element(2, Scenario),
Sessions,
Per_session,
1,
[]
),
Latencies = latencies(Samples, []),
Total_events = erlang:length(Samples),
Total_latency_ms = sum_latencies(Samples, 0),
Total_payload_bytes = sum_payload_bytes(Samples, 0),
Total_patch_ops = sum_patch_ops(Samples, 0),
Peak_memory_units_per_session = peak_memory_units(Samples, 0),
Avg_payload_bytes = divide_safe(Total_payload_bytes, Total_events),
Avg_patch_ops = divide_safe(Total_patch_ops, Total_events),
Throughput = divide_safe(Total_events * 1000, max_int(Total_latency_ms, 1)),
{scenario_report,
erlang:element(2, Scenario),
Sessions,
Per_session,
Total_events,
percentile(Latencies, 50),
percentile(Latencies, 95),
Total_payload_bytes,
Avg_payload_bytes,
Total_patch_ops,
Avg_patch_ops,
Peak_memory_units_per_session,
Peak_memory_units_per_session * Sessions,
Throughput}.
-file("src/lightspeed/ops/performance_harness.gleam", 222).
-spec run_scenarios(profile(), list(scenario()), list(scenario_report())) -> list(scenario_report()).
run_scenarios(Profile, Suite, Reports_rev) ->
case Suite of
[] ->
lists:reverse(Reports_rev);
[Scenario | Rest] ->
run_scenarios(
Profile,
Rest,
[run_scenario(Profile, Scenario) | Reports_rev]
)
end.
-file("src/lightspeed/ops/performance_harness.gleam", 148).
?DOC(" Run benchmarks for one profile and scenario list.\n").
-spec run_suite_with(profile(), list(scenario())) -> report().
run_suite_with(Profile, Suite) ->
{report, Profile, run_scenarios(Profile, Suite, [])}.
-file("src/lightspeed/ops/performance_harness.gleam", 143).
?DOC(" Run benchmarks with pinned profile and default suite.\n").
-spec run_suite() -> report().
run_suite() ->
run_suite_with(pinned_profile(), default_suite()).
-file("src/lightspeed/ops/performance_harness.gleam", 1043).
-spec bool_to_string(boolean()) -> binary().
bool_to_string(Value) ->
case Value of
true ->
<<"true"/utf8>>;
false ->
<<"false"/utf8>>
end.
-file("src/lightspeed/ops/performance_harness.gleam", 868).
-spec find_scenario(list(scenario_report()), workload()) -> gleam@option:option(scenario_report()).
find_scenario(Scenarios, Workload) ->
case Scenarios of
[] ->
none;
[Scenario | Rest] ->
case erlang:element(2, Scenario) =:= Workload of
true ->
{some, Scenario};
false ->
find_scenario(Rest, Workload)
end
end.
-file("src/lightspeed/ops/performance_harness.gleam", 823).
-spec evaluate_one_budget(list(scenario_report()), budget()) -> budget_result().
evaluate_one_budget(Scenarios, Budget) ->
case find_scenario(Scenarios, erlang:element(2, Budget)) of
none ->
{budget_result,
erlang:element(2, Budget),
false,
<<"missing_scenario"/utf8>>};
{some, Report} ->
Passed_latency = erlang:element(7, Report) =< erlang:element(
3,
Budget
),
Passed_payload = erlang:element(9, Report) =< erlang:element(
4,
Budget
),
Passed_throughput = erlang:element(14, Report) >= erlang:element(
5,
Budget
),
Passed_memory = erlang:element(12, Report) =< erlang:element(
6,
Budget
),
Passed = ((Passed_latency andalso Passed_payload) andalso Passed_throughput)
andalso Passed_memory,
{budget_result, erlang:element(2, Budget), Passed, case Passed of
true ->
<<"within_budget"/utf8>>;
false ->
<<<<<<<<<<<<<<"latency="/utf8,
(bool_to_string(
Passed_latency
))/binary>>/binary,
",payload="/utf8>>/binary,
(bool_to_string(Passed_payload))/binary>>/binary,
",throughput="/utf8>>/binary,
(bool_to_string(Passed_throughput))/binary>>/binary,
",memory="/utf8>>/binary,
(bool_to_string(Passed_memory))/binary>>
end}
end.
-file("src/lightspeed/ops/performance_harness.gleam", 808).
-spec evaluate_budget_loop(
list(scenario_report()),
list(budget()),
list(budget_result())
) -> list(budget_result()).
evaluate_budget_loop(Scenarios, Budgets, Results_rev) ->
case Budgets of
[] ->
lists:reverse(Results_rev);
[Budget | Rest] ->
evaluate_budget_loop(
Scenarios,
Rest,
[evaluate_one_budget(Scenarios, Budget) | Results_rev]
)
end.
-file("src/lightspeed/ops/performance_harness.gleam", 153).
?DOC(" Evaluate one report against one budget list.\n").
-spec evaluate_budget(report(), list(budget())) -> list(budget_result()).
evaluate_budget(Report, Budgets) ->
evaluate_budget_loop(erlang:element(3, Report), Budgets, []).
-file("src/lightspeed/ops/performance_harness.gleam", 161).
?DOC(" Count failing budget checks.\n").
-spec budget_failures(list(budget_result())) -> integer().
budget_failures(Results) ->
case Results of
[] ->
0;
[{budget_result, _, Passed, _} | Rest] ->
case Passed of
true ->
budget_failures(Rest);
false ->
1 + budget_failures(Rest)
end
end.
-file("src/lightspeed/ops/performance_harness.gleam", 173).
?DOC(" Stable workload label.\n").
-spec workload_label(workload()) -> binary().
workload_label(Workload) ->
case Workload of
counter ->
<<"counter"/utf8>>;
crud_table ->
<<"crud_table"/utf8>>;
stream_heavy_list ->
<<"stream_heavy_list"/utf8>>;
form_heavy_flow ->
<<"form_heavy_flow"/utf8>>
end.
-file("src/lightspeed/ops/performance_harness.gleam", 183).
?DOC(" Stable pass/fail label.\n").
-spec budget_label(budget_result()) -> binary().
budget_label(Result) ->
case erlang:element(3, Result) of
true ->
<<"pass"/utf8>>;
false ->
<<"fail"/utf8>>
end.
-file("src/lightspeed/ops/performance_harness.gleam", 1031).
-spec join_with_loop(list(binary()), binary(), binary()) -> binary().
join_with_loop(Values, Separator, Acc) ->
case Values of
[] ->
Acc;
[Entry | Rest] ->
join_with_loop(
Rest,
Separator,
<<<<Acc/binary, Separator/binary>>/binary, Entry/binary>>
)
end.
-file("src/lightspeed/ops/performance_harness.gleam", 1024).
-spec join_with(list(binary()), binary()) -> binary().
join_with(Values, Separator) ->
case Values of
[] ->
<<""/utf8>>;
[First | Rest] ->
join_with_loop(Rest, Separator, First)
end.
-file("src/lightspeed/ops/performance_harness.gleam", 191).
?DOC(" Stable report signature suitable for CI artifact comparisons.\n").
-spec report_signature(report()) -> binary().
report_signature(Report) ->
Profile = <<<<<<<<<<<<(erlang:element(2, erlang:element(2, Report)))/binary,
":cpu="/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(3, erlang:element(2, Report))
))/binary>>/binary,
":io="/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(4, erlang:element(2, Report))
))/binary>>/binary,
":mem="/utf8>>/binary,
(erlang:integer_to_binary(erlang:element(5, erlang:element(2, Report))))/binary>>,
Scenario_signature = begin
_pipe = erlang:element(3, Report),
_pipe@1 = gleam@list:map(
_pipe,
fun(Scenario) ->
<<<<<<<<<<<<<<<<<<<<<<<<(workload_label(
erlang:element(
2,
Scenario
)
))/binary,
":p50="/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(
6,
Scenario
)
))/binary>>/binary,
":p95="/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(
7,
Scenario
)
))/binary>>/binary,
":avg_payload="/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(9, Scenario)
))/binary>>/binary,
":avg_ops="/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(11, Scenario)
))/binary>>/binary,
":mem="/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(12, Scenario)
))/binary>>/binary,
":throughput="/utf8>>/binary,
(erlang:integer_to_binary(erlang:element(14, Scenario)))/binary>>
end
),
join_with(_pipe@1, <<";"/utf8>>)
end,
<<<<Profile/binary, "|"/utf8>>/binary, Scenario_signature/binary>>.