Skip to content

Commit

Permalink
=draw the rest of the owl
Browse files Browse the repository at this point in the history
  • Loading branch information
schurhammer committed Nov 1, 2023
1 parent 831191d commit ab24490
Show file tree
Hide file tree
Showing 355 changed files with 48,169 additions and 0 deletions.
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# gleamy_bench

[![Package Version](https://img.shields.io/hexpm/v/gleamy_bench)](https://hex.pm/packages/gleamy_bench)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/gleamy_bench/)

A library for benchmarking gleam code.

# How To

```rust
import gleamy_bench.{Bench, Function, IPS, Input, Min, P, run, table}

// ..

Bench(
[
Input("n=5", 5),
Input("n=10", 10),
Input("n=15", 15),
],
[
Function("fib1", fib1),
Function("fib2", fib2),
],
)
|> run()
|> table([IPS, Min, P(99)])
|> io.println()

```

A benchmark is defined by giving a list of inputs and a list of functions to run on those inputs. Each input + function combination will be timed.

The inputs should all be the same type, and the functions should all accept that type as the only argument. The return type of the function does not matter, only that they all return the same type.

The `run` function actually runs the benchmark and collects the results.

The `table` function makes a table out of the results. You can choose the list of statistics you would like to include in the table.

The output for this example looks like the following.

```
Input Function IPS Min P99
n=5 fib1 2236277.3002 0.0002 0.0006
n=5 fib2 2493122.7461 0.0002 0.0006
n=10 fib1 750561.7961 0.0010 0.0022
n=10 fib2 2755751.7477 0.0002 0.0005
n=15 fib1 80833.4127 0.0102 0.0184
n=15 fib2 2139409.1371 0.0003 0.0007
```

## Contributing

Suggestions and pull requests are welcome!

## Quick start

```sh
gleam run # Run the project
gleam test # Run the tests
gleam shell # Run an Erlang shell
```

## Installation

If available on Hex this package can be added to your Gleam project:

```sh
gleam add gleamy_bench
```

and its documentation can be found at <https://hexdocs.pm/gleamy_bench>.
Empty file added build/dev/erlang/gleam.lock
Empty file.
157 changes: 157 additions & 0 deletions build/dev/erlang/gleam_stdlib/_gleam_artefacts/gleam@@compile.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#!/usr/bin/env escript

% TODO: Don't concurrently print warnings and errors
% TODO: Some tests

-record(arguments, {lib = "./", out = "./", modules = []}).

main(Args) ->
#arguments{out = Out, lib = Lib, modules = Modules} = parse(Args),
IsElixirModule = fun(Module) ->
filename:extension(Module) =:= ".ex"
end,
{ElixirModules, ErlangModules} = lists:partition(IsElixirModule, Modules),
ok = configure_logging(),
ok = add_lib_to_erlang_path(Lib),
ok = filelib:ensure_dir([Out, $/]),
{ErlangOk, _ErlangBeams} = compile_erlang(ErlangModules, Out),
{ElixirOk, _ElixirBeams} = case ErlangOk of
true -> compile_elixir(ElixirModules, Out);
false -> {false, []}
end,
case ErlangOk and ElixirOk of
true -> ok;
false -> erlang:halt(1)
end.

compile_erlang(Modules, Out) ->
Workers = start_compiler_workers(Out),
ok = producer_loop(Modules, Workers),
collect_results({true, []}).

collect_results(Acc = {Result, Beams}) ->
receive
{compiled, Beam} -> collect_results({Result, [Beam | Beams]});
failed -> collect_results({false, Beams})
after 0 -> Acc
end.

producer_loop([], 0) ->
ok;
producer_loop([], Workers) ->
receive
{work_please, _} -> producer_loop([], Workers - 1)
end;
producer_loop([Module | Modules], Workers) ->
receive
{work_please, Worker} ->
erlang:send(Worker, {module, Module}),
producer_loop(Modules, Workers)
end.

start_compiler_workers(Out) ->
Parent = self(),
NumSchedulers = erlang:system_info(schedulers),
SpawnWorker = fun(_) ->
erlang:spawn_link(fun() -> worker_loop(Parent, Out) end)
end,
lists:foreach(SpawnWorker, lists:seq(1, NumSchedulers)),
NumSchedulers.

worker_loop(Parent, Out) ->
Options = [report_errors, report_warnings, debug_info, {outdir, Out}],
erlang:send(Parent, {work_please, self()}),
receive
{module, Module} ->
log({compiling, Module}),
case compile:file(Module, Options) of
{ok, ModuleName} ->
Beam = filename:join(Out, ModuleName) ++ ".beam",
Message = {compiled, Beam},
log(Message),
erlang:send(Parent, Message);
error ->
log({failed, Module}),
erlang:send(Parent, failed)
end,
worker_loop(Parent, Out)
end.

compile_elixir(Modules, Out) ->
Error = [
"The program elixir was not found. Is it installed?",
$\n,
"Documentation for installing Elixir can be viewed here:",
$\n,
"https://elixir-lang.org/install.html"
],
case Modules of
[] -> {true, []};
_ ->
log({starting, "compiler.app"}),
ok = application:start(compiler),
log({starting, "elixir.app"}),
case application:start(elixir) of
ok -> do_compile_elixir(Modules, Out);
_ ->
io:put_chars(standard_error, [Error, $\n]),
{false, []}
end
end.

do_compile_elixir(Modules, Out) ->
ModuleBins = lists:map(fun(Module) ->
log({compiling, Module}),
list_to_binary(Module)
end, Modules),
OutBin = list_to_binary(Out),
Options = [{dest, OutBin}],
% Silence "redefining module" warnings.
% Compiled modules in the build directory are added to the code path.
% These warnings result from recompiling loaded modules.
% TODO: This line can likely be removed if/when the build directory is cleaned before every compilation.
'Elixir.Code':compiler_options([{ignore_module_conflict, true}]),
case 'Elixir.Kernel.ParallelCompiler':compile_to_path(ModuleBins, OutBin, Options) of
{ok, ModuleAtoms, _} ->
ToBeam = fun(ModuleAtom) ->
Beam = filename:join(Out, atom_to_list(ModuleAtom)) ++ ".beam",
log({compiled, Beam}),
Beam
end,
{true, lists:map(ToBeam, ModuleAtoms)};
{error, Errors, _} ->
% Log all filenames associated with modules that failed to compile.
% Note: The compiler prints compilation errors upon encountering them.
ErrorFiles = lists:usort([File || {File, _, _} <- Errors]),
Log = fun(File) ->
log({failed, binary_to_list(File)})
end,
lists:foreach(Log, ErrorFiles),
{false, []};
_ -> {false, []}
end.

add_lib_to_erlang_path(Lib) ->
code:add_paths(filelib:wildcard([Lib, "/*/ebin"])).

parse(Args) ->
parse(Args, #arguments{}).

parse([], Arguments) ->
Arguments;
parse(["--lib", Lib | Rest], Arguments) ->
parse(Rest, Arguments#arguments{lib = Lib});
parse(["--out", Out | Rest], Arguments) ->
parse(Rest, Arguments#arguments{out = Out});
parse([Module | Rest], Arguments = #arguments{modules = Modules}) ->
parse(Rest, Arguments#arguments{modules = [Module | Modules]}).

configure_logging() ->
Enabled = os:getenv("GLEAM_LOG") /= false,
persistent_term:put(gleam_logging_enabled, Enabled).

log(Term) ->
case persistent_term:get(gleam_logging_enabled) of
true -> erlang:display(Term), ok;
false -> ok
end.
Binary file not shown.
Binary file not shown.
45 changes: 45 additions & 0 deletions build/dev/erlang/gleam_stdlib/_gleam_artefacts/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
-module(gleam@base).
-compile([no_auto_import, nowarn_unused_vars]).

-export([encode64/2, decode64/1, url_encode64/2, url_decode64/1]).

-spec encode64(bitstring(), boolean()) -> binary().
encode64(Input, Padding) ->
Encoded = base64:encode(Input),
case Padding of
true ->
Encoded;

false ->
gleam@string:replace(Encoded, <<"="/utf8>>, <<""/utf8>>)
end.

-spec decode64(binary()) -> {ok, bitstring()} | {error, nil}.
decode64(Encoded) ->
Padded = case gleam@bit_string:byte_size(
gleam@bit_string:from_string(Encoded)
)
rem 4 of
0 ->
Encoded;

N ->
gleam@string:append(
Encoded,
gleam@string:repeat(<<"="/utf8>>, 4 - N)
)
end,
gleam_stdlib:base_decode64(Padded).

-spec url_encode64(bitstring(), boolean()) -> binary().
url_encode64(Input, Padding) ->
_pipe = encode64(Input, Padding),
_pipe@1 = gleam@string:replace(_pipe, <<"+"/utf8>>, <<"-"/utf8>>),
gleam@string:replace(_pipe@1, <<"/"/utf8>>, <<"_"/utf8>>).

-spec url_decode64(binary()) -> {ok, bitstring()} | {error, nil}.
url_decode64(Encoded) ->
_pipe = Encoded,
_pipe@1 = gleam@string:replace(_pipe, <<"-"/utf8>>, <<"+"/utf8>>),
_pipe@2 = gleam@string:replace(_pipe@1, <<"_"/utf8>>, <<"/"/utf8>>),
decode64(_pipe@2).
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
-module(gleam@bit_builder).
-compile([no_auto_import, nowarn_unused_vars]).

-export([append_builder/2, prepend_builder/2, new/0, concat/1, concat_bit_strings/1, from_string/1, prepend_string/2, append_string/2, from_string_builder/1, from_bit_string/1, prepend/2, append/2, to_bit_string/1, byte_size/1]).
-export_type([bit_builder/0]).

-type bit_builder() :: any().

-spec append_builder(bit_builder(), bit_builder()) -> bit_builder().
append_builder(First, Second) ->
gleam_stdlib:iodata_append(First, Second).

-spec prepend_builder(bit_builder(), bit_builder()) -> bit_builder().
prepend_builder(To, Prefix) ->
append_builder(Prefix, To).

-spec new() -> bit_builder().
new() ->
gleam_stdlib:identity([]).

-spec concat(list(bit_builder())) -> bit_builder().
concat(Builders) ->
gleam_stdlib:identity(Builders).

-spec concat_bit_strings(list(bitstring())) -> bit_builder().
concat_bit_strings(Bits) ->
gleam_stdlib:identity(Bits).

-spec from_string(binary()) -> bit_builder().
from_string(String) ->
gleam_stdlib:wrap_list(String).

-spec prepend_string(bit_builder(), binary()) -> bit_builder().
prepend_string(To, Prefix) ->
append_builder(from_string(Prefix), To).

-spec append_string(bit_builder(), binary()) -> bit_builder().
append_string(To, Suffix) ->
append_builder(To, from_string(Suffix)).

-spec from_string_builder(gleam@string_builder:string_builder()) -> bit_builder().
from_string_builder(Builder) ->
gleam_stdlib:wrap_list(Builder).

-spec from_bit_string(bitstring()) -> bit_builder().
from_bit_string(Bits) ->
gleam_stdlib:wrap_list(Bits).

-spec prepend(bit_builder(), bitstring()) -> bit_builder().
prepend(To, Prefix) ->
append_builder(from_bit_string(Prefix), To).

-spec append(bit_builder(), bitstring()) -> bit_builder().
append(To, Suffix) ->
append_builder(To, from_bit_string(Suffix)).

-spec to_bit_string(bit_builder()) -> bitstring().
to_bit_string(Builder) ->
erlang:list_to_bitstring(Builder).

-spec byte_size(bit_builder()) -> integer().
byte_size(Builder) ->
erlang:iolist_size(Builder).
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
-module(gleam@bit_string).
-compile([no_auto_import, nowarn_unused_vars]).

-export([from_string/1, byte_size/1, slice/3, is_utf8/1, to_string/1, concat/1, append/2]).

-spec from_string(binary()) -> bitstring().
from_string(X) ->
gleam_stdlib:identity(X).

-spec byte_size(bitstring()) -> integer().
byte_size(X) ->
erlang:byte_size(X).

-spec slice(bitstring(), integer(), integer()) -> {ok, bitstring()} |
{error, nil}.
slice(String, Position, Length) ->
gleam_stdlib:bit_string_slice(String, Position, Length).

-spec do_is_utf8(bitstring()) -> boolean().
do_is_utf8(Bits) ->
case Bits of
<<>> ->
true;

<<_/utf8, Rest/binary>> ->
do_is_utf8(Rest);

_ ->
false
end.

-spec is_utf8(bitstring()) -> boolean().
is_utf8(Bits) ->
do_is_utf8(Bits).

-spec do_to_string(bitstring()) -> {ok, binary()} | {error, nil}.
do_to_string(Bits) ->
case is_utf8(Bits) of
true ->
{ok, gleam_stdlib:identity(Bits)};

false ->
{error, nil}
end.

-spec to_string(bitstring()) -> {ok, binary()} | {error, nil}.
to_string(Bits) ->
do_to_string(Bits).

-spec concat(list(bitstring())) -> bitstring().
concat(Bit_strings) ->
gleam_stdlib:bit_string_concat(Bit_strings).

-spec append(bitstring(), bitstring()) -> bitstring().
append(First, Second) ->
concat([First, Second]).
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit ab24490

Please sign in to comment.