Last active
August 18, 2024 17:21
-
-
Save shanesveller/9546b6b2e68d4433cac8bcf31f465cf5 to your computer and use it in GitHub Desktop.
Benchmark different techniques to decide a callable module at runtime
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 | |
}) |
Author
shanesveller
commented
Jun 25, 2021
•
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