rychale

rychale

First call to NIF is slow, but the next one is 4x faster

Hi,

I have a NIF function (let’s say FUNC) implemented with C++, which is called approx. 10-20 times a second. Its implementation is quite simple - it just reads a struct by an offset in vector allocated previously in the same NIF and returns something like [{x, [%{y: y}]}], where x and y are integers.

There are another functions in the same NIF I’m calling before the function FUNC updating the vector.

So the pipeline is something like ReadNetworkPacket->Decode->CallOtherNifFunc->CallFUNC.

Every time I call FUNC I call it two times in a row, reading vectorA and vectorB. The problem is that the first call takes 8us and the second is 2 us.

I’ve double-checked the amount of work done is the same every time.

Are there any tools I can use to debug this?

Marked As Solved

ausimian

ausimian

I had a look at this, and could repro approximately the same numbers on my system with the gist you posted.

I think what you are observing here is the impact of cache eviction when the process is unscheduled (i.e. when you reach the reduction limit, or run out of things to do). My basis for this is:

  1. A tight loop, or a small (e.g. 1ms) send_after interval sees no real discrepancy in call latency, i.e. on subsequent loops, the first is as fast as the second, I’m assuming because the second call on the prior iteration was recent enough to keep the caches populated.
  2. A Process.sleep(100) between the first and second call sees the cost of the second call rise to the cost of the first.
  3. I ran another experiment where I launched two Tests.Server processes under a supervisor. The first didn’t take any timings but just called the nif in a tight loop, the second behaved as in your gist, making a pair of calls and then effectively sleeping for 100ms. By making them both run on the same scheduler (--erl "+S 1:1"), both calls in the second GenServer were fast. My assumption is that this is because the first genserver is keeping the relevant caches populated with what the second needs.

I suspect there’s just enough work done in the BEAM between your process yielding and getting rescheduled for either its code (jitted code / nif code) or its data (process stacks / heaps) or both to get evicted to some degree, and you’re observing the cost of bringing it back in to L1.

Also Liked

rychale

rychale

Yes, this is because of L1 and/or interrupts. I’ve run the same test on an isolated CPU core with kernel interrupts pinned to another core and the issue just solved.

Thanks

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

The first time you call any function in an application run with mix it’s slow because mix lazy loads code. I would build a release and see if the problem persists.

hauleth

hauleth

Can you post a code as text. Either pastę it here or use Gist (or other pastebin). I prefer to not fetch untrusted archives from internet.

Where Next?

Popular in Questions Top

aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
beno
I will often find my self writing things similar to: case some_value do nil -> something() "" -> something() _ -> somethi...
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lists...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
Lily
In templates/appointment/index.html.eex: <%= for appointment <- @appointments do %> <tr> <td><%= appoi...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New

Other popular topics Top

hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a > b) do {:ok, "a"} end if (a < b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 43806 214
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement