Page MenuHomePhorge

No OneTemporary

Size
21 KB
Referenced Files
None
Subscribers
None
diff --git a/README.md b/README.md
index 2e214c3..5886716 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,3 @@
-# Limiter
+# Concurrent Limiter
-See the docs in `lib/limiter.ex`.
+See the docs in `lib/concurrent_limiter.ex`.
diff --git a/lib/limiter.ex b/lib/concurrent_limiter.ex
similarity index 79%
rename from lib/limiter.ex
rename to lib/concurrent_limiter.ex
index f8789a4..9776377 100644
--- a/lib/limiter.ex
+++ b/lib/concurrent_limiter.ex
@@ -1,150 +1,152 @@
-defmodule Limiter do
+defmodule ConcurrentLimiter do
require Logger
@moduledoc """
- # Limiter
+ # Concurrent Limiter
A concurrency limiter. Limits the number of concurrent invocations possible, without using a worker pool or different processes.
+ It can be useful in cases where you don't need a worker pool but still being able to limit concurrent calls without much overhead. As it internally uses `persistent_term` to store metadata, and can fallback to ETS tables, it is however not made for a large number of limiters and cannot be used for things like a per-user rate limiter.
+
It supports two storage methods:
* **[atomics](https://erlang.org/doc/man/atomics.html)** recommended and default if your OTP is > 21.2.
- * **[ets](https://erlang.org/doc/man/ets.html)** either with a single table per Limiter (faster) or a shared table.
+ * **[ets](https://erlang.org/doc/man/ets.html)** either with a single table per limiter (faster) or a shared table.
- You would however always want to use atomics, ets is mostly there for backwards compatibility.
+ You would almost always want to use atomics, ets is mostly there for backwards compatibility.
"""
@doc """
- Initializes a `Limiter`.
+ Initializes a `ConcurrentLimiter`.
"""
@spec new(name, max_running, max_waiting, options) :: :ok | {:error, :existing} when name: atom(),
max_running: non_neg_integer(),
max_waiting: non_neg_integer() | :infinity,
options: [option],
option: {:wait, non_neg_integer()} | backend,
backend: :atomics | ets_backend,
- ets_backend: :ets | {:ets, atom()} | {:ets, ets_name :: atom(), ets_options :: []}
+ ets_backend: :ets | {:ets, atom()} | {:ets, atom(), ets_options :: []}
def new(name, max_running, max_waiting, options \\ []) do
- name = atom_name(name)
+ name = prefix_name(name)
if defined?(name) do
{:error, :existing}
else
wait = Keyword.get(options, :wait, 150)
backend = Keyword.get(options, :backend, default_backend())
{:ok, backend} = setup_backend(backend)
:persistent_term.put(name, {__MODULE__, max_running, max_waiting, backend, wait})
:ok
end
end
@spec set(name, new_max_running, new_max_waiting, options) :: :ok | :error when name: atom(),
new_max_running: non_neg_integer(),
new_max_waiting: non_neg_integer() | :infinity,
options: [option],
option: {:wait, non_neg_integer()}
@doc "Adjust the limiter limits at runtime"
def set(name, new_max_running, new_max_waiting, options \\ []) do
- name = atom_name(name)
+ name = prefix_name(name)
if defined?(name) do
new_wait = Keyword.get(options, :wait)
{__MODULE__, max_running, max_waiting, backend, wait} = :persistent_term.get(name)
new = {__MODULE__, new_max_running || max_running, new_max_waiting || max_waiting, backend, new_wait || wait}
:persistent_term.put(name, new)
:ok
else
:error
end
end
@spec limit(atom(), function()) :: {:error, :overload} | any()
@doc "Limits invocation of `fun`."
def limit(name, fun) do
- do_limit(atom_name(name), fun)
+ do_limit(prefix_name(name), fun)
end
defp do_limit(name, fun) do
{__MODULE__, max_running, max_waiting, backend, wait} = :persistent_term.get(name)
max = max_running + max_waiting
counter = inc(backend, name)
cond do
counter <= max_running ->
try do
fun.()
after
dec(backend, name)
end
counter > max ->
dec(backend, name)
{:error, :overload}
counter > max_running ->
wait(backend, name, wait, fun)
end
end
defp wait(backend, name, wait, fun) do
Process.sleep(wait)
dec(backend, name)
do_limit(name, fun)
end
defp inc({:ets, ets}, name) do
:ets.update_counter(ets, name, {2, 1}, {name, 0})
end
defp inc({:atomics, ref}, _) do
:atomics.add_get(ref, 1, 1)
end
defp dec({:ets, ets}, name) do
:ets.update_counter(ets, name, {2, -1}, {name, 0})
end
defp dec({:atomics, ref}, _) do
:atomics.sub_get(ref, 1, 1)
end
- defp atom_name(suffix), do: Module.concat(__MODULE__, suffix)
+ defp prefix_name(suffix), do: Module.concat(__MODULE__, suffix)
defp defined?(name) do
{__MODULE__, _, _, _, _, _} = :persistent_term.get(name)
true
rescue
_ -> false
end
defp default_backend() do
if Code.ensure_loaded?(:atomics) do
:atomics
else
- Logger.debug("Limiter: atomics not available, using ETS backend")
+ Logger.debug("ConcurrentLimiter: atomics not available, using ETS backend")
:ets
end
end
defp setup_backend(:ets) do
setup_backend({:ets, ETS})
end
defp setup_backend({:ets, name}) do
setup_backend({:ets, name, [{:write_concurrency, true}, {:read_concurrency, true}]})
end
defp setup_backend({:ets, name, options}) do
- ets_name = atom_name(name)
+ ets_name = prefix_name(name)
case :ets.whereis(ets_name) do
:undefined -> :ets.new(ets_name, [:public, :named_table] ++ options)
_ -> nil
end
{:ok, {:ets, ets_name}}
end
defp setup_backend(:atomics) do
{:ok, {:atomics, :atomics.new(1, [signed: true])}}
end
end
diff --git a/mix.exs b/mix.exs
index 0ff4802..49378b6 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,26 +1,26 @@
-defmodule Limiter.MixProject do
+defmodule ConcurrentLimiter.MixProject do
use Mix.Project
def project do
[
- app: :limiter,
+ app: :concurrent_limiter,
version: "0.1.0",
elixir: "~> 1.10",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
def application do
[
extra_applications: [:logger]
]
end
defp deps do
[
{:ex_doc, "~> 0.21", only: :dev, runtime: false},
{:benchee, "~> 1.0", only: [:dev, :test]}
]
end
end
diff --git a/test/limiter_test.exs b/test/concurrent_limiter_test.exs
similarity index 79%
rename from test/limiter_test.exs
rename to test/concurrent_limiter_test.exs
index 1b81ead..e1d281e 100644
--- a/test/limiter_test.exs
+++ b/test/concurrent_limiter_test.exs
@@ -1,42 +1,42 @@
-defmodule LimiterTest do
+defmodule ConcurrentLimiterTest do
use ExUnit.Case
- doctest Limiter
+ doctest ConcurrentLimiter
test "limiter ets is atomic" do
name = "test1"
- Limiter.new(name, 2, 2)
+ ConcurrentLimiter.new(name, 2, 2)
atomic_test(name)
end
test "limiter atomics is atomic" do
name = "test2"
- Limiter.new(name, 2, 2, backend: :atomics)
+ ConcurrentLimiter.new(name, 2, 2, backend: :atomics)
atomic_test(name)
end
defp atomic_test(name) do
self = self()
sleepy = fn sleep ->
- case Limiter.limit(name, fn ->
+ case ConcurrentLimiter.limit(name, fn ->
send(self, :ok)
Process.sleep(sleep)
:ok
end) do
:ok -> :ok
other -> send(self, other)
end
end
spawn_link(fn -> sleepy.(500) end)
spawn_link(fn -> sleepy.(500) end)
spawn_link(fn -> sleepy.(500) end)
spawn_link(fn -> sleepy.(500) end)
spawn_link(fn -> sleepy.(500) end)
assert_receive :ok, 2000
assert_receive :ok, 2000
assert_receive {:error, :overload}, 2000
assert_receive :ok, 2000
assert_receive :ok, 2000
end
end
diff --git a/test/samples/limiter.exs b/test/samples/limiter.exs
index 785c85f..f903658 100644
--- a/test/samples/limiter.exs
+++ b/test/samples/limiter.exs
@@ -1,28 +1,28 @@
infinite = 1_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000
-Limiter.new(:bench, infinite, 0)
-Limiter.new(:bench_s, infinite, 0, ets: LimiterTest)
+ConcurrentLimiter.new(:bench, infinite, 0)
+ConcurrentLimiter.new(:bench_s, infinite, 0, ets: ConcurrentLimiterTest)
concurrent = [{:read_concurrency, true}, {:write_concurrency, true}]
-Limiter.new(:bench_rw, infinite, 0)
-Limiter.new(:bench_s_rw, infinite, 0, ets: LimiterTest, ets_opts: concurrent)
+ConcurrentLimiter.new(:bench_rw, infinite, 0)
+ConcurrentLimiter.new(:bench_s_rw, infinite, 0, ets: ConcurrentLimiterTest, ets_opts: concurrent)
single = %{
- "Limiter.limit/2" => fn ->
- Limiter.limit(:bench, fn -> :ok end)
+ "ConcurrentLimiter.limit/2" => fn ->
+ ConcurrentLimiter.limit(:bench, fn -> :ok end)
end,
- "Limiter.limit/2 with concurrency" => fn ->
- Limiter.limit(:bench_rw, fn -> :ok end)
+ "ConcurrentLimiter.limit/2 with concurrency" => fn ->
+ ConcurrentLimiter.limit(:bench_rw, fn -> :ok end)
end,
- "Limiter:limit/2 with shared ets" => fn ->
- Limiter.limit(:bench_s, fn -> :ok end)
+ "ConcurrentLimiter:limit/2 with shared ets" => fn ->
+ ConcurrentLimiter.limit(:bench_s, fn -> :ok end)
end,
- "Limiter:limit/2 with shared ets and concurrency" => fn ->
- Limiter.limit(:bench_s_rw, fn -> :ok end)
+ "ConcurrentLimiter:limit/2 with shared ets and concurrency" => fn ->
+ ConcurrentLimiter.limit(:bench_s_rw, fn -> :ok end)
end
}
IO.puts("\n\n\n\nsingle, sequential\n\n\n\n")
Benchee.run(single, parallel: 1)
IO.puts("\n\n\n\nsingle, parallel\n\n\n\n")
Benchee.run(single, parallel: System.schedulers_online())
diff --git a/test/samples/multi_limiter.exs b/test/samples/multi_limiter.exs
index abec65d..f56cb6c 100644
--- a/test/samples/multi_limiter.exs
+++ b/test/samples/multi_limiter.exs
@@ -1,56 +1,74 @@
infinite = 1_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000
+parallel = case Integer.parse(System.get_env("PARALLEL", "")) do
+ {int, _} -> int
+ _ -> System.schedulers_online()/2
+end
-Limiter.new(:bench_u_0, infinite, 0, backend: {:ets, LimiterTest0, []})
-Limiter.new(:bench_u_1, infinite, 0, backend: {:ets, LimiterTest1, []})
-Limiter.new(:bench_u_2, infinite, 0, backend: {:ets, LimiterTest2, []})
-Limiter.new(:bench_u_3, infinite, 0, backend: {:ets, LimiterTest3, []})
+multi_count = case Integer.parse(System.get_env("MULTI", "")) do
+ {int, _} -> int
+ _ -> parallel
+end
-Limiter.new(:bench_a_0, infinite, 0, backend: :atomics)
-Limiter.new(:bench_a_1, infinite, 0, backend: :atomics)
-Limiter.new(:bench_a_2, infinite, 0, backend: :atomics)
-Limiter.new(:bench_a_3, infinite, 0, backend: :atomics)
+names = fn(prefix) ->
+ for i <- 1..multi_count do
+ Module.concat(MultiConcurrentLimiterBenchmark, "#{prefix}#{i}")
+ end
+end
+
+
+bench_unique = for name <- names.("u") do
+ ConcurrentLimiter.new(name, infinite, 0, backend: {:ets, name, []})
+ name
+end
+
+IO.inspect(bench_unique)
+
+bench_atomics = for name <- names.("a") do
+ ConcurrentLimiter.new(name, infinite, 0, backend: :atomics)
+ name
+end
-Limiter.new(:bench_s_0, infinite, 0, backend: {:ets, LimiterTest, []})
-Limiter.new(:bench_s_1, infinite, 0, backend: {:ets, LimiterTest, []})
-Limiter.new(:bench_s_2, infinite, 0, backend: {:ets, LimiterTest, []})
-Limiter.new(:bench_s_3, infinite, 0, backend: {:ets, LimiterTest, []})
+bench_shared = for name <- names.("s") do
+ ConcurrentLimiter.new(name, infinite, 0, backend: {:ets, ConcurrentLimiterTest, []})
+ name
+end
rw = [{:read_concurrency, true}, {:write_concurrency, true}]
-Limiter.new(:bench_u_rw0, infinite, 0, backend: {:ets, LimiterTestRW0, rw})
-Limiter.new(:bench_u_rw1, infinite, 0, backend: {:ets, LimiterTestRW1, rw})
-Limiter.new(:bench_u_rw2, infinite, 0, backend: {:ets, LimiterTestRW2, rw})
-Limiter.new(:bench_u_rw3, infinite, 0, backend: {:ets, LimiterTestRW3, rw})
+bench_unique_rw = for name <- names.("u_rw") do
+ ConcurrentLimiter.new(name, infinite, 0, backend: {:ets, name, rw})
+ name
+end
-Limiter.new(:bench_s_rw0, infinite, 0, backend: {:ets, LimiterTestRW, rw})
-Limiter.new(:bench_s_rw1, infinite, 0, backend: {:ets, LimiterTestRW, rw})
-Limiter.new(:bench_s_rw2, infinite, 0, backend: {:ets, LimiterTestRW, rw})
-Limiter.new(:bench_s_rw3, infinite, 0, backend: {:ets, LimiterTestRW, rw})
+bench_shared_rw = for name <- names.("s_rw") do
+ ConcurrentLimiter.new(name, infinite, 0, backend: {:ets, ConcurrentLimiterTestRW, rw})
+ name
+end
multiple = %{
- "Limiter.limit/2 unique ets" => fn ->
- limiter = Enum.random([:bench_u_0, :bench_u_1, :bench_u_2, :bench_u_3])
- Limiter.limit(limiter, fn -> :ok end)
+ "ConcurrentLimiter.limit/2 unique ets" => fn ->
+ limiter = Enum.random(bench_unique)
+ ConcurrentLimiter.limit(limiter, fn -> :ok end)
end,
- "Limiter:limit/2 shared ets" => fn ->
- limiter = Enum.random([:bench_s_0, :bench_s_1, :bench_s_2, :bench_s_3])
- Limiter.limit(limiter, fn -> :ok end)
+ "ConcurrentLimiter:limit/2 shared ets" => fn ->
+ limiter = Enum.random(bench_shared)
+ ConcurrentLimiter.limit(limiter, fn -> :ok end)
end,
- "Limiter.limit/2 unique ets, concurrency" => fn ->
- limiter = Enum.random([:bench_u_rw0, :bench_u_rw1, :bench_u_rw2, :bench_u_rw3])
- Limiter.limit(limiter, fn -> :ok end)
+ "ConcurrentLimiter.limit/2 unique ets, concurrency" => fn ->
+ limiter = Enum.random(bench_unique_rw)
+ ConcurrentLimiter.limit(limiter, fn -> :ok end)
end,
- "Limiter:limit/2 shared ets, concurrency" => fn ->
- limiter = Enum.random([:bench_s_rw0, :bench_s_rw1, :bench_s_rw2, :bench_s_rw3])
- Limiter.limit(limiter, fn -> :ok end)
+ "ConcurrentLimiter:limit/2 shared ets, concurrency" => fn ->
+ limiter = Enum.random(bench_shared_rw)
+ ConcurrentLimiter.limit(limiter, fn -> :ok end)
end,
- "Limiter:limit/2 atomics" => fn ->
- limiter = Enum.random([:bench_a_0, :bench_a_1, :bench_a_2, :bench_a_3])
- Limiter.limit(limiter, fn -> :ok end)
+ "ConcurrentLimiter:limit/2 atomics" => fn ->
+ limiter = Enum.random(bench_atomics)
+ ConcurrentLimiter.limit(limiter, fn -> :ok end)
end
}
IO.puts("\n\n\n\nmulti, sequential\n\n\n\n")
Benchee.run(multiple)
IO.puts("\n\n\n\nmulti, parallel\n\n\n\n")
Benchee.run(multiple, parallel: System.schedulers_online())
diff --git a/test/samples/results_multi_limiter.txt b/test/samples/results_multi_limiter.txt
index 112c325..75704da 100644
--- a/test/samples/results_multi_limiter.txt
+++ b/test/samples/results_multi_limiter.txt
@@ -1,86 +1,86 @@
multi, sequential
Operating System: Linux
CPU Information: AMD EPYC 7401P 24-Core Processor
Number of Available Cores: 8
Available memory: 31.41 GB
Elixir 1.10.3
Erlang 22.3.2
Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
parallel: 1
inputs: none specified
Estimated total run time: 35 s
-Benchmarking Limiter.limit/2 unique ets...
-Benchmarking Limiter.limit/2 unique ets, concurrency...
-Benchmarking Limiter:limit/2 atomics...
-Benchmarking Limiter:limit/2 shared ets...
-Benchmarking Limiter:limit/2 shared ets, concurrency...
+Benchmarking ConcurrentLimiter.limit/2 unique ets...
+Benchmarking ConcurrentLimiter.limit/2 unique ets, concurrency...
+Benchmarking ConcurrentLimiter:limit/2 atomics...
+Benchmarking ConcurrentLimiter:limit/2 shared ets...
+Benchmarking ConcurrentLimiter:limit/2 shared ets, concurrency...
Name ips average deviation median 99th %
-Limiter:limit/2 atomics 491.88 K 2.03 μs ±1506.30% 1.55 μs 3.48 μs
-Limiter.limit/2 unique ets 414.63 K 2.41 μs ±1169.34% 1.97 μs 4.53 μs
-Limiter:limit/2 shared ets 411.43 K 2.43 μs ±1286.95% 1.96 μs 3.66 μs
-Limiter.limit/2 unique ets, concurrency 406.50 K 2.46 μs ±1006.31% 2.06 μs 4.34 μs
-Limiter:limit/2 shared ets, concurrency 384.04 K 2.60 μs ±1293.25% 2.12 μs 4.37 μs
+ConcurrentLimiter:limit/2 atomics 491.88 K 2.03 μs ±1506.30% 1.55 μs 3.48 μs
+ConcurrentLimiter.limit/2 unique ets 414.63 K 2.41 μs ±1169.34% 1.97 μs 4.53 μs
+ConcurrentLimiter:limit/2 shared ets 411.43 K 2.43 μs ±1286.95% 1.96 μs 3.66 μs
+ConcurrentLimiter.limit/2 unique ets, concurrency 406.50 K 2.46 μs ±1006.31% 2.06 μs 4.34 μs
+ConcurrentLimiter:limit/2 shared ets, concurrency 384.04 K 2.60 μs ±1293.25% 2.12 μs 4.37 μs
Comparison:
-Limiter:limit/2 atomics 491.88 K
-Limiter.limit/2 unique ets 414.63 K - 1.19x slower +0.38 μs
-Limiter:limit/2 shared ets 411.43 K - 1.20x slower +0.40 μs
-Limiter.limit/2 unique ets, concurrency 406.50 K - 1.21x slower +0.43 μs
-Limiter:limit/2 shared ets, concurrency 384.04 K - 1.28x slower +0.57 μs
+ConcurrentLimiter:limit/2 atomics 491.88 K
+ConcurrentLimiter.limit/2 unique ets 414.63 K - 1.19x slower +0.38 μs
+ConcurrentLimiter:limit/2 shared ets 411.43 K - 1.20x slower +0.40 μs
+ConcurrentLimiter.limit/2 unique ets, concurrency 406.50 K - 1.21x slower +0.43 μs
+ConcurrentLimiter:limit/2 shared ets, concurrency 384.04 K - 1.28x slower +0.57 μs
multi, parallel
Operating System: Linux
CPU Information: AMD EPYC 7401P 24-Core Processor
Number of Available Cores: 8
Available memory: 31.41 GB
Elixir 1.10.3
Erlang 22.3.2
Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
parallel: 8
inputs: none specified
Estimated total run time: 35 s
-Benchmarking Limiter.limit/2 unique ets...
-Benchmarking Limiter.limit/2 unique ets, concurrency...
-Benchmarking Limiter:limit/2 atomics...
-Benchmarking Limiter:limit/2 shared ets...
-Benchmarking Limiter:limit/2 shared ets, concurrency...
+Benchmarking ConcurrentLimiter.limit/2 unique ets...
+Benchmarking ConcurrentLimiter.limit/2 unique ets, concurrency...
+Benchmarking ConcurrentLimiter:limit/2 atomics...
+Benchmarking ConcurrentLimiter:limit/2 shared ets...
+Benchmarking ConcurrentLimiter:limit/2 shared ets, concurrency...
Name ips average deviation median 99th %
-Limiter:limit/2 atomics 307.84 K 3.25 μs ±1113.62% 2.09 μs 10.24 μs
-Limiter.limit/2 unique ets, concurrency 95.56 K 10.46 μs ±391.37% 2.93 μs 163.02 μs
-Limiter:limit/2 shared ets, concurrency 92.39 K 10.82 μs ±374.36% 2.92 μs 158.97 μs
-Limiter.limit/2 unique ets 80.68 K 12.39 μs ±362.74% 2.85 μs 160.66 μs
-Limiter:limit/2 shared ets 6.04 K 165.66 μs ±17.23% 167.48 μs 237.96 μs
+ConcurrentLimiter:limit/2 atomics 307.84 K 3.25 μs ±1113.62% 2.09 μs 10.24 μs
+ConcurrentLimiter.limit/2 unique ets, concurrency 95.56 K 10.46 μs ±391.37% 2.93 μs 163.02 μs
+ConcurrentLimiter:limit/2 shared ets, concurrency 92.39 K 10.82 μs ±374.36% 2.92 μs 158.97 μs
+ConcurrentLimiter.limit/2 unique ets 80.68 K 12.39 μs ±362.74% 2.85 μs 160.66 μs
+ConcurrentLimiter:limit/2 shared ets 6.04 K 165.66 μs ±17.23% 167.48 μs 237.96 μs
Comparison:
-Limiter:limit/2 atomics 307.84 K
-Limiter.limit/2 unique ets, concurrency 95.56 K - 3.22x slower +7.22 μs
-Limiter:limit/2 shared ets, concurrency 92.39 K - 3.33x slower +7.57 μs
-Limiter.limit/2 unique ets 80.68 K - 3.82x slower +9.15 μs
-Limiter:limit/2 shared ets 6.04 K - 51.00x slower +162.41 μs
+ConcurrentLimiter:limit/2 atomics 307.84 K
+ConcurrentLimiter.limit/2 unique ets, concurrency 95.56 K - 3.22x slower +7.22 μs
+ConcurrentLimiter:limit/2 shared ets, concurrency 92.39 K - 3.33x slower +7.57 μs
+ConcurrentLimiter.limit/2 unique ets 80.68 K - 3.82x slower +9.15 μs
+ConcurrentLimiter:limit/2 shared ets 6.04 K - 51.00x slower +162.41 μs
diff --git a/test/samples/update_counter.exs b/test/samples/update_counter.exs
index 1768735..6310c57 100644
--- a/test/samples/update_counter.exs
+++ b/test/samples/update_counter.exs
@@ -1,19 +1,20 @@
:ets.new(:limiter_bench, [:public, :named_table])
+:ets.new(:limiter_bench_concurrent, [:public, :named_table, {:read_concurrency, false}, {:write_concurrency, true}])
+atomics = :atomics.new(1, [])
-Benchee.run(
+update_counter =
%{
"ets:update_counter" => fn ->
:ets.update_counter(:limiter_bench, "bench", {2, 1}, {"bench", 0})
- end
- },
- parallel: 1
-)
-
-Benchee.run(
- %{
- "ets:update_counter" => fn ->
+ end,
+ "ets:update_counter concurrent" => fn ->
:ets.update_counter(:limiter_bench, "bench", {2, 1}, {"bench", 0})
- end
- },
- parallel: System.schedulers_online()
-)
+ end,
+ "atomics:add_get" => fn ->
+ :atomics.add_get(atomics, 1, 1)
+ end,
+ }
+
+Benchee.run(update_counter, parallel: 1)
+Benchee.run(update_counter, parallel: System.schedulers_online())
+

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jan 21, 3:24 PM (1 d, 7 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
55725
Default Alt Text
(21 KB)

Event Timeline