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 | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Wrapped each with
for _n <- 1..1000, do: ...
: