munksgaard

munksgaard

NIF returns incorrect results, reuses binaries?

I’m currently in the very early stages of implementing futlixir, a bridge that enables Elixir to call out to Futhark programs. It works by taking a Futhark library and generating a corresponding NIF, similar to how Rustler lets you use Rust programs.

Futlixir is still in a very early stage, but I can currently create arrays and call simple functions with those arrays. Here is an example of what the Elixir code looks like:

c("lib_map.ex") # Import Map.NIF
{:ok, cfg} = Map.NIF.futhark_context_config_new()
{:ok, ctx} = Map.NIF.futhark_context_new(cfg)

xs_binary = <<0, 1>>
{:ok, xs} = Map.NIF.futhark_new_u8_1d(ctx, xs_binary)
{:ok, ^xs_binary} = Map.NIF.futhark_u8_1d_to_binary(ctx, xs)
{:ok, ys} = Map.NIF.futhark_new_u8_1d(ctx, <<1, 4>>)
{:ok, zs} = Map.NIF.futhark_entry_add(ctx, xs, ys)
{:ok, <<1, 5>> = zs_binary} = Map.NIF.futhark_u8_1d_to_binary(ctx, zs)

xs_binary = <<1::integer-signed-64-little>>
{:ok, xs} = Map.NIF.futhark_new_i64_1d(ctx, xs_binary)
{:ok, ^xs_binary} = Map.NIF.futhark_i64_1d_to_binary(ctx, xs)
{:ok, ys} = Map.NIF.futhark_new_i64_1d(ctx, <<1279::integer-signed-64-little>>)
{:ok, zs} = Map.NIF.futhark_entry_add_i64(ctx, xs, ys)
{:ok, <<1280::integer-signed-64-little>> = zs_binary} = Map.NIF.futhark_i64_1d_to_binary(ctx, zs)

The program is very simple, first it creates two arrays of bytes (u8) and adds them together, yielding <<1,5>>, next it takes two arrays of i64 and adds them together, yielding <<1280::integer-signed-64-little>> (the array has size 1).

The problem arises because sometimes the call to Map.NIF.futhark_i64_1d_to_binary will return <<2, 0, 0, 0, 0, 0, 0, 0>>, or indeed whatever xs_binary is set to, instead of the correct result. For some reason, it seems like it’s not correctly allocating a new binary for zs_binary but reusing xs_binary? This only happens some times, and only if the preceding block of code is included as well (the one adding two u8 arrays).

The definition of futhark_i64_1d_to_binary looks like this:

static ERL_NIF_TERM futhark_i64_1d_to_binary_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
  struct futhark_context **ctx;
  struct futhark_i64_1d **xs;

  ErlNifBinary binary;
  ERL_NIF_TERM ret;

  if(argc != 2) {
    return enif_make_badarg(env);
  }

  if(!enif_get_resource(env, argv[0], CONTEXT_TYPE, (void**) &ctx)) {
    return enif_make_badarg(env);
  }

  if(!enif_get_resource(env, argv[1], I64_1D, (void**) &xs)) {
    return enif_make_badarg(env);
  }

  const int64_t *shape = futhark_shape_i64_1d(*ctx, *xs);

  enif_alloc_binary(shape[0] * sizeof(int64_t), &binary);

  if (futhark_values_i64_1d(*ctx, *xs, (int64_t *)(binary.data)) != 0) return enif_make_badarg(env);
  futhark_context_sync(*ctx);

  ret = enif_make_binary(env, &binary);

  return enif_make_tuple2(env, atom_ok, ret);
}

Does anyone have a clue what might be wrong?

I’ve uploaded all the files needed to reproduce the issue here: lib_map.c · GitHub

To compile and run it, run the following commands:

gcc -Wall -shared -o lib_map_nif.so -fPIC lib_map_nif.c -lOpenCL -lm
iex --dot-iex test.exs

You’ll probably need to run it a handful of times for the error to trigger.

Marked As Solved

munksgaard

munksgaard

Turns out that the _new calls in futhark (e.g. futhark_new_i64_1d) are asynchronous, so I needed to insert an explicit synchronization point after creating arrays and before handing off control to the BEAM.

Also Liked

tj0

tj0

enif_alloc_binary could be re-using the memory of the previous answer. You may need to zero it / memset.

Otherwise, there’s too many pointers references for me to reason about easily.

Edit: Looked closer, not sure if that’s it. But there’s something funny going on here. Maybe check the result of
futhark_values_i64_1d with some type of assert? Set the binary value to 0 beforehand and check that it has changed before futhark_context_sync. Of course, it could be something completely different, but I think that’s a reasonable starting point.

Where Next?

Popular in Questions Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
Werner
Hi, I’m using Ubuntu 18.04 and after updating to OTP-24.0 yesterday i have this warning when I run “mix local.hex”: 14:57:30.512 [warn] ...
New
sacepums
Hey guys. I'm new to elixir and im really stocked about it. But I ran into a bit of problem - I need to convert a date sting, for examp...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
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
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
Exadra37
Sometimes I want to check if the input into a function is not a blank string. My first approach: defmodule Example do def do_stuff(s...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New

Other popular topics Top

JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1140 51847 244
New
William
I would like to know that is there any online source for learning Phoenix Framework for building E-Commerce Store? Any advantage on build...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New

We're in Beta

About us Mission Statement