Packages
Tensor library for Gleam/BEAM with a pure Gleam API, zero-copy views, and optional native acceleration
Current section
Files
Jump to
Current section
Files
src/viva_tensor@bench@nif.erl
-module(viva_tensor@bench@nif).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/viva_tensor/bench/nif.gleam").
-export([main/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(false).
-file("src/viva_tensor/bench/nif.gleam", 388).
?DOC(false).
-spec repeat_char(binary(), integer()) -> binary().
repeat_char(Char, N) ->
case N =< 0 of
true ->
<<""/utf8>>;
false ->
<<Char/binary, (repeat_char(Char, N - 1))/binary>>
end.
-file("src/viva_tensor/bench/nif.gleam", 381).
?DOC(false).
-spec string_length(binary()) -> integer().
string_length(S) ->
string:length(S).
-file("src/viva_tensor/bench/nif.gleam", 368).
?DOC(false).
-spec float_to_str(float()) -> binary().
float_to_str(F) ->
Rounded = begin
_pipe = erlang:round(F * 100.0),
erlang:float(_pipe)
end,
Result = Rounded / 100.0,
gleam_stdlib:float_to_string(Result).
-file("src/viva_tensor/bench/nif.gleam", 349).
?DOC(false).
-spec pad_speedup(float()) -> binary().
pad_speedup(Speedup) ->
Str = <<(float_to_str(Speedup))/binary, "x"/utf8>>,
Len = string_length(Str),
Padding = 12 - Len,
<<Str/binary, (repeat_char(<<" "/utf8>>, Padding))/binary>>.
-file("src/viva_tensor/bench/nif.gleam", 356).
?DOC(false).
-spec format_time(float()) -> binary().
format_time(Ms) ->
case Ms < 1.0 of
true ->
<<(float_to_str(Ms * 1000.0))/binary, " μs"/utf8>>;
false ->
case Ms < 1000.0 of
true ->
<<(float_to_str(Ms))/binary, " ms"/utf8>>;
false ->
<<(float_to_str(Ms / 1000.0))/binary, " s"/utf8>>
end
end.
-file("src/viva_tensor/bench/nif.gleam", 342).
?DOC(false).
-spec pad_time(float()) -> binary().
pad_time(Ms) ->
Str = format_time(Ms),
Len = string_length(Str),
Padding = 12 - Len,
<<Str/binary, (repeat_char(<<" "/utf8>>, Padding))/binary>>.
-file("src/viva_tensor/bench/nif.gleam", 395).
?DOC(false).
-spec now_microseconds() -> integer().
now_microseconds() ->
viva_tensor@core@ffi:now_microseconds().
-file("src/viva_tensor/bench/nif.gleam", 337).
?DOC(false).
-spec random_floats(integer()) -> list(float()).
random_floats(N) ->
_pipe = gleam@list:range(0, N - 1),
gleam@list:map(_pipe, fun(_) -> viva_tensor@core@ffi:random_uniform() end).
-file("src/viva_tensor/bench/nif.gleam", 277).
?DOC(false).
-spec benchmark_sum(integer()) -> nil.
benchmark_sum(Size) ->
gleam_stdlib:println(
<<" Vector size: "/utf8, (erlang:integer_to_binary(Size))/binary>>
),
gleam_stdlib:println(
<<" ┌────────────────────┬──────────────┬──────────────┐"/utf8>>
),
gleam_stdlib:println(
<<" │ Backend │ Time │ Speedup │"/utf8>>
),
gleam_stdlib:println(
<<" ├────────────────────┼──────────────┼──────────────┤"/utf8>>
),
Data = random_floats(Size),
Start_pure = now_microseconds(),
Arr = viva_tensor@core@ffi:list_to_array(Data),
_ = viva_tensor@core@ffi:array_sum(Arr),
End_pure = now_microseconds(),
Pure_time = erlang:float(End_pure - Start_pure) / 1000.0,
gleam_stdlib:println(
<<<<" │ Pure Erlang │ "/utf8, (pad_time(Pure_time))/binary>>/binary,
" │ 1.00x │"/utf8>>
),
Start_c = now_microseconds(),
_ = viva_tensor@core@ffi:nif_sum(Data),
End_c = now_microseconds(),
C_time = erlang:float(End_c - Start_c) / 1000.0,
C_speedup = case C_time > +0.0 of
true ->
case C_time of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Pure_time / Gleam@denominator
end;
false ->
+0.0
end,
gleam_stdlib:println(
<<<<<<<<" │ Apple Accelerate │ "/utf8, (pad_time(C_time))/binary>>/binary,
" │ "/utf8>>/binary,
(pad_speedup(C_speedup))/binary>>/binary,
" │"/utf8>>
),
Start_zig = now_microseconds(),
_ = viva_tensor@core@ffi:zig_sum(Data),
End_zig = now_microseconds(),
Zig_time = erlang:float(End_zig - Start_zig) / 1000.0,
Zig_speedup = case Zig_time > +0.0 of
true ->
case Zig_time of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> Pure_time / Gleam@denominator@1
end;
false ->
+0.0
end,
gleam_stdlib:println(
<<<<<<<<" │ Zig SIMD │ "/utf8, (pad_time(Zig_time))/binary>>/binary,
" │ "/utf8>>/binary,
(pad_speedup(Zig_speedup))/binary>>/binary,
" │"/utf8>>
),
gleam_stdlib:println(
<<" └────────────────────┴──────────────┴──────────────┘"/utf8>>
),
gleam_stdlib:println(<<""/utf8>>).
-file("src/viva_tensor/bench/nif.gleam", 215).
?DOC(false).
-spec benchmark_dot(integer()) -> nil.
benchmark_dot(Size) ->
gleam_stdlib:println(
<<" Vector size: "/utf8, (erlang:integer_to_binary(Size))/binary>>
),
gleam_stdlib:println(
<<" ┌────────────────────┬──────────────┬──────────────┐"/utf8>>
),
gleam_stdlib:println(
<<" │ Backend │ Time │ Speedup │"/utf8>>
),
gleam_stdlib:println(
<<" ├────────────────────┼──────────────┼──────────────┤"/utf8>>
),
A_data = random_floats(Size),
B_data = random_floats(Size),
Start_pure = now_microseconds(),
A_arr = viva_tensor@core@ffi:list_to_array(A_data),
B_arr = viva_tensor@core@ffi:list_to_array(B_data),
_ = viva_tensor@core@ffi:array_dot(A_arr, B_arr),
End_pure = now_microseconds(),
Pure_time = erlang:float(End_pure - Start_pure) / 1000.0,
gleam_stdlib:println(
<<<<" │ Pure Erlang │ "/utf8, (pad_time(Pure_time))/binary>>/binary,
" │ 1.00x │"/utf8>>
),
Start_c = now_microseconds(),
_ = viva_tensor@core@ffi:nif_dot(A_data, B_data),
End_c = now_microseconds(),
C_time = erlang:float(End_c - Start_c) / 1000.0,
C_speedup = case C_time > +0.0 of
true ->
case C_time of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Pure_time / Gleam@denominator
end;
false ->
+0.0
end,
gleam_stdlib:println(
<<<<<<<<" │ Apple Accelerate │ "/utf8, (pad_time(C_time))/binary>>/binary,
" │ "/utf8>>/binary,
(pad_speedup(C_speedup))/binary>>/binary,
" │"/utf8>>
),
Start_zig = now_microseconds(),
_ = viva_tensor@core@ffi:zig_dot(A_data, B_data),
End_zig = now_microseconds(),
Zig_time = erlang:float(End_zig - Start_zig) / 1000.0,
Zig_speedup = case Zig_time > +0.0 of
true ->
case Zig_time of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> Pure_time / Gleam@denominator@1
end;
false ->
+0.0
end,
gleam_stdlib:println(
<<<<<<<<" │ Zig SIMD │ "/utf8, (pad_time(Zig_time))/binary>>/binary,
" │ "/utf8>>/binary,
(pad_speedup(Zig_speedup))/binary>>/binary,
" │"/utf8>>
),
gleam_stdlib:println(
<<" └────────────────────┴──────────────┴──────────────┘"/utf8>>
),
gleam_stdlib:println(<<""/utf8>>).
-file("src/viva_tensor/bench/nif.gleam", 195).
?DOC(false).
-spec time_zig_matmul(
list(float()),
list(float()),
integer(),
integer(),
integer()
) -> {list(float()), float()}.
time_zig_matmul(A, B, M, N, K) ->
Start = now_microseconds(),
Result = viva_tensor@core@ffi:zig_matmul(A, B, M, N, K),
End = now_microseconds(),
case Result of
{ok, Data} ->
{Data, erlang:float(End - Start) / 1000.0};
{error, _} ->
{[], erlang:float(End - Start) / 1000.0}
end.
-file("src/viva_tensor/bench/nif.gleam", 179).
?DOC(false).
-spec time_c_matmul(
list(float()),
list(float()),
integer(),
integer(),
integer()
) -> {list(float()), float()}.
time_c_matmul(A, B, M, N, K) ->
Start = now_microseconds(),
Result = viva_tensor@core@ffi:nif_matmul(A, B, M, N, K),
End = now_microseconds(),
case Result of
{ok, Data} ->
{Data, erlang:float(End - Start) / 1000.0};
{error, _} ->
{[], erlang:float(End - Start) / 1000.0}
end.
-file("src/viva_tensor/bench/nif.gleam", 163).
?DOC(false).
-spec time_pure_matmul(
list(float()),
list(float()),
integer(),
integer(),
integer()
) -> {list(float()), float()}.
time_pure_matmul(A, B, M, N, K) ->
Start = now_microseconds(),
A_arr = viva_tensor@core@ffi:list_to_array(A),
B_arr = viva_tensor@core@ffi:list_to_array(B),
Result_arr = viva_tensor@core@ffi:array_matmul(A_arr, B_arr, M, N, K),
Result = viva_tensor@core@ffi:array_to_list(Result_arr),
End = now_microseconds(),
{Result, erlang:float(End - Start) / 1000.0}.
-file("src/viva_tensor/bench/nif.gleam", 107).
?DOC(false).
-spec benchmark_matmul(integer(), integer(), integer()) -> nil.
benchmark_matmul(M, N, K) ->
Size_str = <<<<<<<<<<<<(erlang:integer_to_binary(M))/binary, "x"/utf8>>/binary,
(erlang:integer_to_binary(K))/binary>>/binary,
" @ "/utf8>>/binary,
(erlang:integer_to_binary(K))/binary>>/binary,
"x"/utf8>>/binary,
(erlang:integer_to_binary(N))/binary>>,
gleam_stdlib:println(<<" Matrix: "/utf8, Size_str/binary>>),
gleam_stdlib:println(
<<" ┌────────────────────┬──────────────┬──────────────┐"/utf8>>
),
gleam_stdlib:println(
<<" │ Backend │ Time │ Speedup │"/utf8>>
),
gleam_stdlib:println(
<<" ├────────────────────┼──────────────┼──────────────┤"/utf8>>
),
A_data = random_floats(M * K),
B_data = random_floats(K * N),
{_, Pure_time} = time_pure_matmul(A_data, B_data, M, N, K),
gleam_stdlib:println(
<<<<" │ Pure Erlang │ "/utf8, (pad_time(Pure_time))/binary>>/binary,
" │ 1.00x │"/utf8>>
),
{_, C_time} = time_c_matmul(A_data, B_data, M, N, K),
C_speedup = case C_time > +0.0 of
true ->
case C_time of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Pure_time / Gleam@denominator
end;
false ->
+0.0
end,
gleam_stdlib:println(
<<<<<<<<" │ Apple Accelerate │ "/utf8, (pad_time(C_time))/binary>>/binary,
" │ "/utf8>>/binary,
(pad_speedup(C_speedup))/binary>>/binary,
" │"/utf8>>
),
{_, Zig_time} = time_zig_matmul(A_data, B_data, M, N, K),
Zig_speedup = case Zig_time > +0.0 of
true ->
case Zig_time of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> Pure_time / Gleam@denominator@1
end;
false ->
+0.0
end,
gleam_stdlib:println(
<<<<<<<<" │ Zig SIMD │ "/utf8, (pad_time(Zig_time))/binary>>/binary,
" │ "/utf8>>/binary,
(pad_speedup(Zig_speedup))/binary>>/binary,
" │"/utf8>>
),
gleam_stdlib:println(
<<" └────────────────────┴──────────────┴──────────────┘"/utf8>>
),
gleam_stdlib:println(<<""/utf8>>).
-file("src/viva_tensor/bench/nif.gleam", 374).
?DOC(false).
-spec bool_to_string(boolean()) -> binary().
bool_to_string(B) ->
case B of
true ->
<<"Yes ✓"/utf8>>;
false ->
<<"No ✗"/utf8>>
end.
-file("src/viva_tensor/bench/nif.gleam", 20).
?DOC(false).
-spec main() -> nil.
main() ->
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(
<<"╔═══════════════════════════════════════════════════════════════════════════╗"/utf8>>
),
gleam_stdlib:println(
<<"║ viva_tensor - THREE-WAY NIF BENCHMARK ║"/utf8>>
),
gleam_stdlib:println(
<<"║ Pure Erlang vs Apple Accelerate vs Zig SIMD ║"/utf8>>
),
gleam_stdlib:println(
<<"╚═══════════════════════════════════════════════════════════════════════════╝"/utf8>>
),
gleam_stdlib:println(<<""/utf8>>),
C_nif_loaded = viva_tensor@core@ffi:is_nif_loaded(),
Zig_nif_loaded = viva_tensor@core@ffi:zig_is_loaded(),
C_backend = viva_tensor@core@ffi:nif_backend_info(),
Zig_backend = viva_tensor@core@ffi:zig_backend_info(),
gleam_stdlib:println(<<"━━━ BACKEND STATUS ━━━"/utf8>>),
gleam_stdlib:println(
<<" Apple Accelerate NIF: "/utf8,
(bool_to_string(C_nif_loaded))/binary>>
),
gleam_stdlib:println(<<" Backend: "/utf8, C_backend/binary>>),
gleam_stdlib:println(
<<" Zig SIMD NIF: "/utf8, (bool_to_string(Zig_nif_loaded))/binary>>
),
gleam_stdlib:println(<<" Backend: "/utf8, Zig_backend/binary>>),
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(
<<"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"/utf8>>
),
gleam_stdlib:println(
<<" MATRIX MULTIPLICATION"/utf8>>
),
gleam_stdlib:println(
<<"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"/utf8>>
),
gleam_stdlib:println(<<""/utf8>>),
benchmark_matmul(32, 32, 32),
benchmark_matmul(64, 64, 64),
benchmark_matmul(128, 128, 128),
benchmark_matmul(256, 256, 256),
gleam_stdlib:println(
<<"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"/utf8>>
),
gleam_stdlib:println(<<" DOT PRODUCT"/utf8>>),
gleam_stdlib:println(
<<"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"/utf8>>
),
gleam_stdlib:println(<<""/utf8>>),
benchmark_dot(10000),
benchmark_dot(100000),
gleam_stdlib:println(
<<"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"/utf8>>
),
gleam_stdlib:println(<<" SUM"/utf8>>),
gleam_stdlib:println(
<<"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"/utf8>>
),
gleam_stdlib:println(<<""/utf8>>),
benchmark_sum(100000),
benchmark_sum(1000000),
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(
<<"═══════════════════════════════════════════════════════════════════════════"/utf8>>
),
gleam_stdlib:println(
<<" BENCHMARK COMPLETE!"/utf8>>
),
gleam_stdlib:println(
<<"═══════════════════════════════════════════════════════════════════════════"/utf8>>
).