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

hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
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
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New

Other popular topics Top

Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 130286 1222
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
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

We're in Beta

About us Mission Statement