Skip to content

Instantly share code, notes, and snippets.

@rychale
Created February 13, 2022 18:44
Show Gist options
  • Save rychale/8531fbecd7cd3e8e04da977f8ab0e513 to your computer and use it in GitHub Desktop.
Save rychale/8531fbecd7cd3e8e04da977f8ab0e513 to your computer and use it in GitHub Desktop.
Calling a simple NIF function every 0.1 seconds two times in a row results in different latency
defmodule Test.NativeProcessor.Lib do
@moduledoc false
@on_load :load_nifs
def load_nifs do
path = Application.app_dir(:test, "priv/native") |> String.to_charlist()
:erlang.load_nif(path, 0)
end
def orders() do
exit(:nif_not_loaded)
end
end
defmodule Test.NativeProcessor.Client do
@moduledoc false
alias Test.NativeProcessor.Lib
@impl true
def orders(), do: Lib.orders()
end
defmodule Test.Server do
use GenServer
alias Test.NativeProcessor.Client
def init(init_arg) do
send(self(), :orders)
{:ok, init_arg}
end
@impl true
def handle_info(:orders, state) do
orders()
{:noreply, state}
end
defp orders() do
timeit(:first, fn -> Client.orders() end)
timeit(:second, fn -> Client.orders() end)
Process.send_after(self(), :orders, 100)
end
defp now(), do: System.monotonic_time()
defp timeit(name, f) do
s = now()
r = f.()
e = now()
IO.puts(
"Calling a function #{inspect(name)} took #{inspect(System.convert_time_unit(e - s, :native, :nanosecond))} nsec"
)
r
end
end
defmodule Test.Main do
@moduledoc false
use Application
@impl true
def start(_type, _args) do
GenServer.start_link(Test.Server, [])
end
end
CXX ?= g++
ERLANG_PATH = $(shell erl -eval 'io:format("~s", [lists:concat([code:root_dir(), "/erts-", erlang:system_info(version), "/include"])])' -s init stop -noshell)
OBJECTS = $(patsubst c_libs/%.cpp,build/%.o,$(wildcard c_libs/*.cpp))
all: priv/native.so
dirs:
mkdir -p build
clean:
rm -rf priv/native.so build
priv/native.so: $(OBJECTS) | dirs
echo $(OBJECTS)
$(CXX) -Ofast -shared -fdiagnostics-color=always -o $@ $^
build/%.o: c_libs/%.cpp
$(CXX) -Ofast -std=c++20 -c -shared -Wall -fPIC -I $(ERLANG_PATH) -fdiagnostics-color=always -o $@ $<
defmodule Test.MixProject do
use Mix.Project
def project do
[
app: :test,
version: "0.1.0",
elixir: "~> 1.13",
start_permanent: Mix.env() == :prod,
deps: deps(),
compilers: [:elixir_make] ++ Mix.compilers(),
releases: [
prod: [
strip_beams: false,
config_providers: [
{TomlConfigProvider, path: {:system, "RELEASE_CONFIG_PATH"}},
],
]
],
]
end
def application do
[
mod: {Test.Main, []},
extra_applications: [:logger]
]
end
defp deps do
[
{:elixir_make, "~> 0.4", runtime: false}
]
end
end
#include <erl_nif.h>
class MyPriv
{
ERL_NIF_TERM atom_ok_;
public:
MyPriv(ErlNifEnv* env):
atom_ok_(enif_make_atom(env, "ok"))
{
}
inline const auto& atom_ok() const
{
return this->atom_ok_;
}
};
ERL_NIF_TERM orders(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) noexcept
{
auto priv = reinterpret_cast<MyPriv*>(enif_priv_data(env));
return priv->atom_ok();
}
static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) noexcept
{
*priv_data = new MyPriv(env);
return 0;
}
static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) noexcept
{
return load(env, priv_data, load_info);
}
static void unload(ErlNifEnv* env, void* priv_data) noexcept
{
delete reinterpret_cast<MyPriv*>(enif_priv_data(env));
}
static ErlNifFunc nif_funcs[] =
{
{"orders", 0, orders}
};
ERL_NIF_INIT(Elixir.Test.NativeProcessor.Lib, nif_funcs, load, NULL, upgrade, unload)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment