Skip to content

Commit

Permalink
Rework mongoose_mam_id as a persistent_term with an atomic
Browse files Browse the repository at this point in the history
```elixir
  def next_id() do
    Benchee.run(
      %{
        "native code" => fn list ->
          Enum.map(list, fn t -> :mongoose_mam_id.next_unique(t) end)
        end,
        "pure erlang" => fn list ->
          Enum.map(list, fn t -> :mongoose_mam_id_2.next_unique(t) end)
        end,
      },
      inputs: %{
        "1M monotonic" => monotonic_list(100_000_000)
      },
      formatters:
      [
        {Benchee.Formatters.Console, extended_statistics: true}
      ],
      parallel: 1,
      time: 5,
      warmup: 0
    )
  end

  defp monotonic_list(size) do
    fun = fn(_, [head | _] = acc) ->
      next = head + :rand.uniform(20) - 1
      [next | acc]
    end
    :lists.reverse(:lists.foldl(fun, [:rand.uniform(20)], :lists.seq(1, size)))
  end
```

returns

```
Operating System: macOS
CPU Information: Apple M3 Pro
Number of Available Cores: 12
Available memory: 36 GB
Elixir 1.17.3
Erlang 27.1.2
JIT enabled: true

Benchmark suite executing with the following configuration:
warmup: 0 ns
time: 5 s
memory time: 0 ns
reduction time: 0 ns
parallel: 1
inputs: 1M monotonic
Estimated total run time: 10 s

Benchmarking native code with input 1M monotonic ...
Benchmarking pure erlang with input 1M monotonic ...
Calculating statistics...
Formatting results...

Name                  ips        average  deviation         median         99th %
pure erlang          0.31         3.23 s    ±10.49%         3.23 s         3.47 s
native code          0.28         3.54 s     ±6.26%         3.54 s         3.70 s

Comparison:
pure erlang          0.31
native code          0.28 - 1.10x slower +0.32 s

Extended statistics:

Name                minimum        maximum    sample size                     mode
pure erlang          2.99 s         3.47 s              2                     None
native code          3.39 s         3.70 s              2                     None
```
  • Loading branch information
NelsonVides committed Dec 4, 2024
1 parent 33fe8d6 commit 0874522
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 74 deletions.
48 changes: 0 additions & 48 deletions c_src/mongoose_mam_id.cpp

This file was deleted.

5 changes: 0 additions & 5 deletions rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@
{httpd_util, integer_to_hexlist, 1}
]}.

{port_specs,
[
{".*", "priv/lib/mongoose_mam_id.so", ["c_src/mongoose_mam_id.cpp"], [{env, [{"CXXFLAGS", "$CXXFLAGS -O3 -std=c++11"}]}]}
]}.

{require_min_otp_vsn, "21"}.

%% We agreed to use https:// for deps because of possible firewall issues.
Expand Down
18 changes: 1 addition & 17 deletions src/ejabberd.erl
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@

-module(ejabberd).
-author('[email protected]').
-export([get_pid_file/0,
get_status_file/0,
get_so_path/0]).
-export([get_pid_file/0, get_status_file/0]).

-type lang() :: binary().

Expand All @@ -41,20 +39,6 @@

-export_type([lang/0, xml_stream_item/0]).

-spec get_so_path() -> binary() | string().
get_so_path() ->
case os:getenv("EJABBERD_SO_PATH") of
false ->
case code:priv_dir(mongooseim) of
{error, _} ->
".";
Path ->
filename:join([Path, "lib"])
end;
Path ->
Path
end.

-spec get_pid_file() -> 'false' | nonempty_string().
get_pid_file() ->
case os:getenv("EJABBERD_PID_PATH") of
Expand Down
27 changes: 23 additions & 4 deletions src/mam/mongoose_mam_id.erl
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,28 @@
-export([next_unique/1]).
-on_load(load/0).

-spec load() -> ok.
load() ->
Path = filename:join(ejabberd:get_so_path(), ?MODULE_STRING),
erlang:load_nif(Path, 0).
case persistent_term:get(?MODULE, undefined) of
undefined ->
Atomic = atomics:new(1, [{signed, false}]),
persistent_term:put(?MODULE, Atomic);
_ ->
ok
end.

next_unique(_Candidate) ->
erlang:nif_error(not_loaded).
-spec next_unique(integer()) -> integer().
next_unique(Candidate) when is_integer(Candidate), 0 < Candidate ->
Atomic = persistent_term:get(?MODULE),
next_unique(Candidate, Candidate - 1, Atomic).

-spec next_unique(integer(), integer(), atomics:atomics_ref()) -> integer().
next_unique(Candidate, Current, Atomic) ->
case atomics:compare_exchange(Atomic, 1, Current, Candidate) of
Int when is_integer(Int), Candidate =< Int ->
next_unique(Int + 1, Int, Atomic);
Int when is_integer(Int) ->
next_unique(Candidate, Int, Atomic);
ok ->
Candidate
end.

0 comments on commit 0874522

Please sign in to comment.