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@gflops.erl
-module(viva_tensor@bench@gflops).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/viva_tensor/bench/gflops.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/gflops.gleam", 369).
?DOC(false).
-spec repeat_string(binary(), integer()) -> binary().
repeat_string(S, N) ->
case N =< 0 of
true ->
<<""/utf8>>;
false ->
<<S/binary, (repeat_string(S, N - 1))/binary>>
end.
-file("src/viva_tensor/bench/gflops.gleam", 360).
?DOC(false).
-spec pad_right(binary(), integer()) -> binary().
pad_right(S, Width) ->
Len = string:length(S),
Padding = Width - Len,
case Padding > 0 of
true ->
<<S/binary, (repeat_string(<<" "/utf8>>, Padding))/binary>>;
false ->
S
end.
-file("src/viva_tensor/bench/gflops.gleam", 354).
?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/gflops.gleam", 293).
?DOC(false).
-spec print_gflops_row(binary(), float(), float()) -> nil.
print_gflops_row(Name, Time_ms, Gflops) ->
Time_str = case Time_ms > +0.0 of
true ->
float_to_str(Time_ms);
false ->
<<"N/A"/utf8>>
end,
Gflops_str = case Gflops > +0.0 of
true ->
float_to_str(Gflops);
false ->
<<"N/A"/utf8>>
end,
gleam_stdlib:println(
<<<<<<<<<<<<" │ "/utf8, (pad_right(Name, 18))/binary>>/binary,
" │ "/utf8>>/binary,
(pad_right(Time_str, 12))/binary>>/binary,
" │ "/utf8>>/binary,
(pad_right(Gflops_str, 12))/binary>>/binary,
" │"/utf8>>
).
-file("src/viva_tensor/bench/gflops.gleam", 286).
?DOC(false).
-spec compute_gflops(integer(), float()) -> float().
compute_gflops(Flops, Time_ms) ->
case Time_ms > +0.0 of
true ->
case (Time_ms * 1000000.0) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> erlang:float(Flops) / Gleam@denominator
end;
false ->
+0.0
end.
-file("src/viva_tensor/bench/gflops.gleam", 317).
?DOC(false).
-spec time_it(fun(() -> LAD)) -> {float(), LAD}.
time_it(F) ->
Start = viva_tensor@core@ffi:now_microseconds(),
Result = F(),
End = viva_tensor@core@ffi:now_microseconds(),
Time_ms = erlang:float(End - Start) / 1000.0,
{Time_ms, Result}.
-file("src/viva_tensor/bench/gflops.gleam", 325).
?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/gflops.gleam", 231).
?DOC(false).
-spec benchmark_gflops(integer(), integer(), integer()) -> nil.
benchmark_gflops(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>>,
Flops = ((2 * M) * N) * K,
gleam_stdlib:println(
<<<<<<<<" "/utf8, Size_str/binary>>/binary, " ("/utf8>>/binary,
(erlang:integer_to_binary(Flops))/binary>>/binary,
" FLOPs):"/utf8>>
),
gleam_stdlib:println(
<<" ┌────────────────────┬──────────────┬──────────────┐"/utf8>>
),
gleam_stdlib:println(
<<" │ Backend │ Time (ms) │ GFLOPS │"/utf8>>
),
gleam_stdlib:println(
<<" ├────────────────────┼──────────────┼──────────────┤"/utf8>>
),
A_data = random_floats(M * K),
B_data = random_floats(K * N),
A_arr = viva_tensor@core@ffi:list_to_array(A_data),
B_arr = viva_tensor@core@ffi:list_to_array(B_data),
{Pure_time, _} = time_it(
fun() -> viva_tensor@core@ffi:array_matmul(A_arr, B_arr, M, N, K) end
),
Pure_gflops = compute_gflops(Flops, Pure_time),
print_gflops_row(<<"Pure Erlang"/utf8>>, Pure_time, Pure_gflops),
case viva_tensor@core@ffi:is_nif_loaded() of
true ->
{Accel_time, _} = time_it(
fun() ->
viva_tensor@core@ffi:nif_matmul(A_data, B_data, M, N, K)
end
),
Accel_gflops = compute_gflops(Flops, Accel_time),
print_gflops_row(
<<"Apple Accelerate"/utf8>>,
Accel_time,
Accel_gflops
);
false ->
print_gflops_row(<<"Apple Accelerate"/utf8>>, +0.0, +0.0)
end,
case viva_tensor@core@ffi:zig_is_loaded() of
true ->
{Zig_time, _} = time_it(
fun() ->
viva_tensor@core@ffi:zig_matmul(A_data, B_data, M, N, K)
end
),
Zig_gflops = compute_gflops(Flops, Zig_time),
print_gflops_row(<<"Zig SIMD"/utf8>>, Zig_time, Zig_gflops);
false ->
print_gflops_row(<<"Zig SIMD"/utf8>>, +0.0, +0.0)
end,
gleam_stdlib:println(
<<" └────────────────────┴──────────────┴──────────────┘"/utf8>>
),
gleam_stdlib:println(<<""/utf8>>).
-file("src/viva_tensor/bench/gflops.gleam", 347).
?DOC(false).
-spec result_to_string(boolean()) -> binary().
result_to_string(Ok) ->
case Ok of
true ->
<<"✓ PASS"/utf8>>;
false ->
<<"✗ FAIL"/utf8>>
end.
-file("src/viva_tensor/bench/gflops.gleam", 339).
?DOC(false).
-spec float_close(float(), float(), float()) -> boolean().
float_close(A, B, Tol) ->
Diff = case (A - B) < +0.0 of
true ->
B - A;
false ->
A - B
end,
Diff < Tol.
-file("src/viva_tensor/bench/gflops.gleam", 185).
?DOC(false).
-spec test_sum_correctness() -> nil.
test_sum_correctness() ->
gleam_stdlib:println(<<" Sum Reduction Correctness:"/utf8>>),
Data = [1.0, 2.0, 3.0, 4.0, 5.0],
Expected = 15.0,
Arr = viva_tensor@core@ffi:list_to_array(Data),
Pure_result = viva_tensor@core@ffi:array_sum(Arr),
Pure_ok = float_close(Pure_result, Expected, 1.0e-10),
gleam_stdlib:println(
<<" Pure Erlang: "/utf8, (result_to_string(Pure_ok))/binary>>
),
case viva_tensor@core@ffi:is_nif_loaded() of
true ->
case viva_tensor@core@ffi:nif_sum(Data) of
{ok, Result} ->
Ok = float_close(Result, Expected, 1.0e-10),
gleam_stdlib:println(
<<" Apple Accelerate: "/utf8,
(result_to_string(Ok))/binary>>
);
{error, _} ->
gleam_stdlib:println(
<<" Apple Accelerate: ✗ Error"/utf8>>
)
end;
false ->
gleam_stdlib:println(
<<" Apple Accelerate: - Not available"/utf8>>
)
end,
case viva_tensor@core@ffi:zig_is_loaded() of
true ->
case viva_tensor@core@ffi:zig_sum(Data) of
{ok, Result@1} ->
Ok@1 = float_close(Result@1, Expected, 1.0e-10),
gleam_stdlib:println(
<<" Zig SIMD: "/utf8,
(result_to_string(Ok@1))/binary>>
);
{error, _} ->
gleam_stdlib:println(
<<" Zig SIMD: ✗ Error"/utf8>>
)
end;
false ->
gleam_stdlib:println(
<<" Zig SIMD: - Not available"/utf8>>
)
end.
-file("src/viva_tensor/bench/gflops.gleam", 139).
?DOC(false).
-spec test_dot_correctness() -> nil.
test_dot_correctness() ->
gleam_stdlib:println(<<" Dot Product Correctness:"/utf8>>),
A_data = [1.0, 2.0, 3.0, 4.0],
B_data = [5.0, 6.0, 7.0, 8.0],
Expected = 70.0,
A_arr = viva_tensor@core@ffi:list_to_array(A_data),
B_arr = viva_tensor@core@ffi:list_to_array(B_data),
Pure_result = viva_tensor@core@ffi:array_dot(A_arr, B_arr),
Pure_ok = float_close(Pure_result, Expected, 1.0e-10),
gleam_stdlib:println(
<<" Pure Erlang: "/utf8, (result_to_string(Pure_ok))/binary>>
),
case viva_tensor@core@ffi:is_nif_loaded() of
true ->
case viva_tensor@core@ffi:nif_dot(A_data, B_data) of
{ok, Result} ->
Ok = float_close(Result, Expected, 1.0e-10),
gleam_stdlib:println(
<<" Apple Accelerate: "/utf8,
(result_to_string(Ok))/binary>>
);
{error, _} ->
gleam_stdlib:println(
<<" Apple Accelerate: ✗ Error"/utf8>>
)
end;
false ->
gleam_stdlib:println(
<<" Apple Accelerate: - Not available"/utf8>>
)
end,
case viva_tensor@core@ffi:zig_is_loaded() of
true ->
case viva_tensor@core@ffi:zig_dot(A_data, B_data) of
{ok, Result@1} ->
Ok@1 = float_close(Result@1, Expected, 1.0e-10),
gleam_stdlib:println(
<<" Zig SIMD: "/utf8,
(result_to_string(Ok@1))/binary>>
);
{error, _} ->
gleam_stdlib:println(
<<" Zig SIMD: ✗ Error"/utf8>>
)
end;
false ->
gleam_stdlib:println(
<<" Zig SIMD: - Not available"/utf8>>
)
end,
gleam_stdlib:println(<<""/utf8>>).
-file("src/viva_tensor/bench/gflops.gleam", 330).
?DOC(false).
-spec check_close(list(float()), list(float()), float()) -> boolean().
check_close(Actual, Expected, Tol) ->
case erlang:length(Actual) =:= erlang:length(Expected) of
true ->
_pipe = gleam@list:map2(
Actual,
Expected,
fun(A, E) -> float_close(A, E, Tol) end
),
gleam@list:all(_pipe, fun(X) -> X end);
false ->
false
end.
-file("src/viva_tensor/bench/gflops.gleam", 87).
?DOC(false).
-spec test_matmul_correctness() -> nil.
test_matmul_correctness() ->
gleam_stdlib:println(<<" Matrix Multiplication Correctness:"/utf8>>),
A_data = [1.0, 2.0, 3.0, 4.0],
B_data = [5.0, 6.0, 7.0, 8.0],
Expected = [19.0, 22.0, 43.0, 50.0],
A = gleam@result:unwrap(
viva_tensor@core@tensor:new(A_data, [2, 2]),
viva_tensor@core@tensor:zeros([2, 2])
),
B = gleam@result:unwrap(
viva_tensor@core@tensor:new(B_data, [2, 2]),
viva_tensor@core@tensor:zeros([2, 2])
),
Pure_result = viva_tensor@core@ops:matmul_fast(A, B),
Pure_ok = case Pure_result of
{ok, T} ->
check_close(viva_tensor@core@tensor:to_list(T), Expected, 1.0e-10);
{error, _} ->
false
end,
gleam_stdlib:println(
<<" Pure Erlang: "/utf8, (result_to_string(Pure_ok))/binary>>
),
case viva_tensor@core@ffi:is_nif_loaded() of
true ->
case viva_tensor@core@ffi:nif_matmul(A_data, B_data, 2, 2, 2) of
{ok, Result} ->
Ok = check_close(Result, Expected, 1.0e-10),
gleam_stdlib:println(
<<" Apple Accelerate: "/utf8,
(result_to_string(Ok))/binary>>
);
{error, _} ->
gleam_stdlib:println(
<<" Apple Accelerate: ✗ Error"/utf8>>
)
end;
false ->
gleam_stdlib:println(
<<" Apple Accelerate: - Not available"/utf8>>
)
end,
case viva_tensor@core@ffi:zig_is_loaded() of
true ->
case viva_tensor@core@ffi:zig_matmul(A_data, B_data, 2, 2, 2) of
{ok, Result@1} ->
Ok@1 = check_close(Result@1, Expected, 1.0e-10),
gleam_stdlib:println(
<<" Zig SIMD: "/utf8,
(result_to_string(Ok@1))/binary>>
);
{error, _} ->
gleam_stdlib:println(
<<" Zig SIMD: ✗ Error"/utf8>>
)
end;
false ->
gleam_stdlib:println(
<<" Zig SIMD: - Not available"/utf8>>
)
end,
gleam_stdlib:println(<<""/utf8>>).
-file("src/viva_tensor/bench/gflops.gleam", 21).
?DOC(false).
-spec main() -> nil.
main() ->
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(
<<"╔═══════════════════════════════════════════════════════════════════════════╗"/utf8>>
),
gleam_stdlib:println(
<<"║ viva_tensor - GFLOPS BENCHMARK ║"/utf8>>
),
gleam_stdlib:println(
<<"║ Performance measurement & numerical correctness ║"/utf8>>
),
gleam_stdlib:println(
<<"╚═══════════════════════════════════════════════════════════════════════════╝"/utf8>>
),
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(<<"━━━ BACKEND STATUS ━━━"/utf8>>),
gleam_stdlib:println(viva_tensor@core@ops:all_backends_info()),
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(
<<"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"/utf8>>
),
gleam_stdlib:println(<<" NUMERICAL CORRECTNESS"/utf8>>),
gleam_stdlib:println(
<<"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"/utf8>>
),
gleam_stdlib:println(<<""/utf8>>),
test_matmul_correctness(),
test_dot_correctness(),
test_sum_correctness(),
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(
<<"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"/utf8>>
),
gleam_stdlib:println(<<" GFLOPS MEASUREMENT"/utf8>>),
gleam_stdlib:println(
<<"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"/utf8>>
),
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(<<" Matrix multiplication: 2*M*N*K FLOPs"/utf8>>),
gleam_stdlib:println(<<""/utf8>>),
benchmark_gflops(128, 128, 128),
benchmark_gflops(256, 256, 256),
benchmark_gflops(512, 512, 512),
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(
<<"═══════════════════════════════════════════════════════════════════════════"/utf8>>
),
gleam_stdlib:println(<<" BENCHMARK COMPLETE!"/utf8>>),
gleam_stdlib:println(
<<"═══════════════════════════════════════════════════════════════════════════"/utf8>>
).