Created
February 13, 2022 18:44
-
-
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
This file contains hidden or 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
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 |
This file contains hidden or 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
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 $@ $< |
This file contains hidden or 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
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 |
This file contains hidden or 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
#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