Skip to content

Instantly share code, notes, and snippets.

@shanesveller
Last active June 25, 2021 20:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shanesveller/9546b6b2e68d4433cac8bcf31f465cf5 to your computer and use it in GitHub Desktop.
Save shanesveller/9546b6b2e68d4433cac8bcf31f465cf5 to your computer and use it in GitHub Desktop.
# Inspired by https://engineering.tripping.com/blazing-fast-elixir-configuration-475aca10011d
# Updated with more options, including persistent_term and process locals
Mix.install([{:benchee, "~> 1.0"}])
Application.put_env(:fast_config, :adapter, TargetModule)
:persistent_term.put({:fast_config, :adapter}, TargetModule)
defmodule TargetModule do
def run, do: :ok
end
defmodule DispatchedModule do
@callback adapter :: module
@callback run :: term
end
defmodule ApplicationAdapter do
@behaviour DispatchedModule
@impl true
def adapter, do: Application.get_env(:fast_config, :adapter)
@impl true
def run, do: adapter().run()
end
defmodule PersistentAdapter do
@behaviour DispatchedModule
@impl true
def adapter, do: :persistent_term.get({:fast_config, :adapter})
@impl true
def run, do: adapter().run()
end
defmodule MemoizedAdapter do
@behaviour DispatchedModule
@impl true
def adapter, do: memoized_get(:fast_config, :adapter)
@impl true
def run, do: adapter().run()
defp memoized_get(namespace, key) do
case :persistent_term.get({namespace, key}) do
nil ->
val = Application.get_env(namespace, key)
:persistent_term.put({namespace, key}, val)
val
val ->
val
end
end
end
defmodule ProcessAdapter do
@behaviour DispatchedModule
@impl true
def adapter, do: memoized_get(:fast_config, :adapter)
@impl true
def run, do: adapter().run()
defp memoized_get(namespace, key) do
case Process.get({namespace, key}) do
nil ->
val = Application.get_env(namespace, key)
Process.put({namespace, key}, val)
val
val ->
val
end
end
end
defmodule ModuleAttrAdapter do
@behaviour DispatchedModule
@adapter Application.compile_env(:fast_config, :adapter)
@impl true
def adapter, do: @adapter
@impl true
def run, do: adapter().run()
end
Benchee.run(%{
"application.get_env" => fn -> ApplicationAdapter.run() end,
"persistent_term.get" => fn -> PersistentAdapter.run() end,
"memoized" => fn -> MemoizedAdapter.run() end,
"process_memoized" => fn -> ProcessAdapter.run() end,
"module_attr" => fn -> ModuleAttrAdapter.run() end,
"baseline" => fn -> TargetModule.run() end
})
@shanesveller
Copy link
Author

shanesveller commented Jun 25, 2021

Operating System: macOS
CPU Information: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Number of Available Cores: 12
Available memory: 32 GB
Elixir 1.12.1
Erlang 23.2.3

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: 42 s

Benchmarking application.get_env...
Benchmarking baseline...
Benchmarking memoized...
Benchmarking module_attr...
Benchmarking persistent_term.get...
Benchmarking process_memoized...

Name                          ips        average  deviation         median         99th %
module_attr                6.11 M      163.53 ns  ±2259.68%           0 ns        1000 ns
baseline                   6.06 M      165.04 ns   ±272.46%           0 ns        1000 ns
memoized                   4.60 M      217.46 ns  ±1876.85%           0 ns        1000 ns
persistent_term.get        4.58 M      218.52 ns  ±1873.46%           0 ns        1000 ns
process_memoized           4.36 M      229.33 ns  ±1892.28%           0 ns        1000 ns
application.get_env        2.13 M      470.12 ns  ±5196.54%           0 ns        1000 ns

Comparison:
module_attr                6.11 M
baseline                   6.06 M - 1.01x slower +1.51 ns
memoized                   4.60 M - 1.33x slower +53.93 ns
persistent_term.get        4.58 M - 1.34x slower +54.99 ns
process_memoized           4.36 M - 1.40x slower +65.80 ns
application.get_env        2.13 M - 2.87x slower +306.59 ns

@shanesveller
Copy link
Author

Wrapped each with for _n <- 1..1000, do: ...:

Name                          ips        average  deviation         median         99th %
baseline                  39.81 K       25.12 μs    ±22.03%          24 μs          47 μs
module_attr               29.99 K       33.35 μs    ±20.00%          33 μs          64 μs
process_memoized          12.83 K       77.95 μs    ±16.34%          75 μs         141 μs
persistent_term.get       11.24 K       88.97 μs    ±10.66%          87 μs         130 μs
memoized                  10.58 K       94.48 μs    ±13.80%          92 μs         165 μs
application.get_env        3.86 K      259.22 μs    ±11.20%         253 μs         403 μs

Comparison:
baseline                  39.81 K
module_attr               29.99 K - 1.33x slower +8.23 μs
process_memoized          12.83 K - 3.10x slower +52.83 μs
persistent_term.get       11.24 K - 3.54x slower +63.85 μs
memoized                  10.58 K - 3.76x slower +69.36 μs
application.get_env        3.86 K - 10.32x slower +234.10 μs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment