pelemay icon indicating copy to clipboard operation
pelemay copied to clipboard

Support elem function

Open zacky1972 opened this issue 4 years ago • 2 comments

Is your feature request related to a problem? Please describe.

In conv2d: https://github.com/zeam-vm/conv2d

To enhance to use Pelemay:

git diff:

diff --git a/lib/conv2d.ex b/lib/conv2d.ex
index c6dd785..aeb15da 100644
--- a/lib/conv2d.ex
+++ b/lib/conv2d.ex
@@ -1,4 +1,7 @@
 defmodule Conv2d do
+  require Pelemay
+  import Pelemay
+
   @moduledoc """
   Documentation for Conv2d.
   """
@@ -93,7 +96,7 @@ defmodule Conv2d do
     t_input = input |> dup(m) |> t1
     t_weight = weight |> t1
 
-    mult = Enum.zip(t_input, t_weight) |> Enum.map(& elem(&1, 0) * elem(&1, 1))
+    mult = Enum.zip(t_input, t_weight) |> map_mult()
 
     mult
     |> Enum.chunk_every(x * y)
@@ -105,6 +108,12 @@ defmodule Conv2d do
     |> List.flatten
   end
 
+  defpelemay do
+    def map_mult list do
+      list |> Enum.map(& elem(&1, 0) * elem(&1, 1))      
+    end
+  end
+
   @doc """
 
   ## Examples
diff --git a/mix.exs b/mix.exs
index aa86e28..af448e2 100644
--- a/mix.exs
+++ b/mix.exs
@@ -4,8 +4,8 @@ defmodule Conv2d.MixProject do
   def project do
     [
       app: :conv2d,
-      version: "0.0.1",
-      elixir: "~> 1.7",
+      version: "0.0.2",
+      elixir: "~> 1.9",
       start_permanent: Mix.env() == :prod,
       deps: deps()
     ]
@@ -21,6 +21,8 @@ defmodule Conv2d.MixProject do
   # Run "mix help deps" to learn about dependencies.
   defp deps do
     [
+      {:pelemay, "~> 0.0.2"},
+      {:benchfella, "~> 0.3.0", only: :dev},
       # {:dep_from_hexpm, "~> 0.3.0"},
       # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
     ]

The following error occurs:

$ mix compile
Compiling 1 file (.ex)

== Compilation error in file lib/conv2d.ex ==
** (ArgumentError) cannot pipe list into elem(&1, 0) * elem(&1, 1), the :* operator can only take two arguments
    (elixir) lib/macro.ex:155: Macro.pipe/3
    (stdlib) lists.erl:1263: :lists.foldl/3
    (elixir) lib/enum.ex:1336: Enum."-map/2-lists^map/1-0-"/2
    lib/sum_mag.ex:261: SumMag.optimize_func/2
    (elixir) lib/enum.ex:1336: Enum."-map/2-lists^map/1-0-"/2
    lib/sum_mag.ex:253: SumMag.map/2
    expanding macro: Pelemay.defpelemay/1
    lib/conv2d.ex:111: Conv2d (module)

Describe the solution you'd like Support elem function in defpelemay.

Describe alternatives you've considered No idea.

Additional context None.

zacky1972 avatar Sep 22 '19 22:09 zacky1972

@hisaway I'll write ideal generated native code, soon later.

zacky1972 avatar Sep 22 '19 23:09 zacky1972

@hisaway The following code is ideal generated native code, though I haven't test yet (new code fragments are enif_get_2_double_vec_from_number_tuple_list and map_mult_elem_2):

// This file was generated by Pelemay.Generator.Native
#include<stdbool.h>
#include<erl_nif.h>
#include<string.h>

static int load(ErlNifEnv *env, void **priv, ERL_NIF_TERM info);
static void unload(ErlNifEnv *env, void *priv);
static int reload(ErlNifEnv *env, void **priv, ERL_NIF_TERM info);
static int upgrade(ErlNifEnv *env, void **priv, void **old_priv, ERL_NIF_TERM info);

static int
load(ErlNifEnv *env, void **priv, ERL_NIF_TERM info)
{
  return 0;
}

static void
unload(ErlNifEnv *env, void *priv)
{
}

static int
reload(ErlNifEnv *env, void **priv, ERL_NIF_TERM info)
{
  return 0;
}

static int
upgrade(ErlNifEnv *env, void **priv, void **old_priv, ERL_NIF_TERM info)
{
  return load(env, priv, info);
}
const int fail = 0;
const int success = 1;
const int empty = 0;
const size_t cache_line_size = 64;
const size_t size_t_max = -1;
const size_t init_size_long = cache_line_size / sizeof(long);
const size_t init_size_double = cache_line_size / sizeof(double);
const size_t size_t_highest_bit = ~(size_t_max >> 1);

#define loop_vectorize_width 4

int enif_get_long_vec_from_list(ErlNifEnv *env, ERL_NIF_TERM list, long **vec, size_t *vec_l);
int enif_get_double_vec_from_list(ErlNifEnv *env, ERL_NIF_TERM list, double **vec, size_t *vec_l);
int enif_get_double_vec_from_number_list(ErlNifEnv *env, ERL_NIF_TERM list, double **vec, size_t *vec_l);
int enif_get_2_double_vec_from_number_tuple_list(ErlNifEnv *env, ERL_NIF_TERM list, double **vec1, double **vec2, size_t *vec_l);

ERL_NIF_TERM enif_make_list_from_long_vec(ErlNifEnv *env, const long *vec, const size_t vec_l);
ERL_NIF_TERM enif_make_list_from_double_vec(ErlNifEnv *env, const double *vec, const size_t vec_l);

ERL_NIF_TERM
enif_make_list_from_long_vec(ErlNifEnv *env, const long *vec, const size_t vec_l)
{
  ERL_NIF_TERM list = enif_make_list(env, 0);
  for(size_t i = vec_l; i > 0; i--) {
    ERL_NIF_TERM tail = list;
    ERL_NIF_TERM head = enif_make_int64(env, vec[i - 1]);
    list = enif_make_list_cell(env, head, tail);
  }
  return list;
}

ERL_NIF_TERM
enif_make_list_from_double_vec(ErlNifEnv *env, const double *vec, const size_t vec_l)
{
  ERL_NIF_TERM list = enif_make_list(env, 0);
  for(size_t i = vec_l; i > 0; i--) {
    ERL_NIF_TERM tail = list;
    ERL_NIF_TERM head = enif_make_double(env, vec[i - 1]);
    list = enif_make_list_cell(env, head, tail);
  }
  return list;
}

int
enif_get_long_vec_from_list(ErlNifEnv *env, ERL_NIF_TERM list, long **vec, size_t *vec_l)
{
  ERL_NIF_TERM head, tail;

  if (__builtin_expect((enif_get_list_cell(env, list, &head, &tail) == fail),
                       true)) {
    if (__builtin_expect((enif_is_empty_list(env, list) == success), true)) {
      *vec_l = empty;
      *vec = NULL;
      return success;
    }
    return fail;
  }
  size_t n = init_size_long;
  size_t nn = cache_line_size;
  long *t = (long *)enif_alloc(nn);
  if (__builtin_expect((t == NULL), false)) {
    return fail;
  }

  size_t i = 0;
  ERL_NIF_TERM tmp[loop_vectorize_width];
  int tmp_r[loop_vectorize_width];
  while (true) {
#pragma clang loop vectorize(disable)
    for (size_t count = 0; count < loop_vectorize_width; count++) {
      tmp[count] = head;
      if (__builtin_expect(
              (enif_get_list_cell(env, tail, &head, &tail) == fail), false)) {
        for (size_t c = 0; c <= count; c++) {
          tmp_r[c] = enif_get_int64(env, tmp[c], &t[i++]);
        }
        int acc = true;
#pragma clang loop vectorize(enable)
        for (size_t c = 0; c <= count; c++) {
          acc &= (tmp_r[c] == success);
        }
        if (__builtin_expect((acc == false), false)) {
          enif_free(t);
          return fail;
        }

        *vec_l = i;
        *vec = t;
        return success;
      }
    }
    if (__builtin_expect((i > size_t_max - loop_vectorize_width), false)) {
      enif_free(t);
      return fail;
    }
    if (__builtin_expect((i + loop_vectorize_width > n), false)) {
      size_t old_nn = nn;
      if (__builtin_expect(((nn & size_t_highest_bit) == 0), true)) {
        nn <<= 1;
        n <<= 1;
      } else {
        nn = size_t_max;
        n = nn / sizeof(long);
      }
      long *new_t = (long *)enif_alloc(nn);
      if(__builtin_expect((new_t == NULL), false)) {
        enif_free(t);
        return fail;
      }
      memcpy(new_t, t, old_nn);
      enif_free(t);
      t = new_t;
    }
#pragma clang loop vectorize(enable) unroll(enable)
    for (size_t count = 0; count < loop_vectorize_width; count++) {
      tmp_r[count] = enif_get_int64(env, tmp[count], &t[i + count]);
    }
    int acc = true;
#pragma clang loop vectorize(enable) unroll(enable)
    for (size_t count = 0; count < loop_vectorize_width; count++) {
      acc &= (tmp_r[count] == success);
    }
    if (__builtin_expect((acc == false), false)) {
      enif_free(t);
      return fail;
    }
    i += loop_vectorize_width;
  }
}

int
enif_get_double_vec_from_list(ErlNifEnv *env, ERL_NIF_TERM list, double **vec, size_t *vec_l)
{
  ERL_NIF_TERM head, tail;

  if (__builtin_expect((enif_get_list_cell(env, list, &head, &tail) == fail),
                       true)) {
    if (__builtin_expect((enif_is_empty_list(env, list) == success), true)) {
      *vec_l = empty;
      *vec = NULL;
      return success;
    }
    return fail;
  }
  size_t n = init_size_long;
  size_t nn = cache_line_size;
  double *t = (double *)enif_alloc(nn);
  if (__builtin_expect((t == NULL), false)) {
    return fail;
  }

  size_t i = 0;
  ERL_NIF_TERM tmp[loop_vectorize_width];
  int tmp_r[loop_vectorize_width];
  while (true) {
#pragma clang loop vectorize(disable)
    for (size_t count = 0; count < loop_vectorize_width; count++) {
      tmp[count] = head;
      if (__builtin_expect(
              (enif_get_list_cell(env, tail, &head, &tail) == fail), false)) {
        for (size_t c = 0; c <= count; c++) {
          tmp_r[c] = enif_get_double(env, tmp[c], &t[i++]);
        }
        int acc = true;
#pragma clang loop vectorize(enable)
        for (size_t c = 0; c <= count; c++) {
          acc &= (tmp_r[c] == success);
        }
        if (__builtin_expect((acc == false), false)) {
          enif_free(t);
          return fail;
        }

        *vec_l = i;
        *vec = t;
        return success;
      }
    }
    if (__builtin_expect((i > size_t_max - loop_vectorize_width), false)) {
      enif_free(t);
      return fail;
    }
    if (__builtin_expect((i + loop_vectorize_width > n), false)) {
      size_t old_nn = nn;
      if (__builtin_expect(((nn & size_t_highest_bit) == 0), true)) {
        nn <<= 1;
        n <<= 1;
      } else {
        nn = size_t_max;
        n = nn / sizeof(long);
      }
      double *new_t = (double *)enif_alloc(nn);
      if(__builtin_expect((new_t == NULL), false)) {
        enif_free(t);
        return fail;
      }
      memcpy(new_t, t, old_nn);
      enif_free(t);
      t = new_t;
    }
#pragma clang loop vectorize(enable) unroll(enable)
    for (size_t count = 0; count < loop_vectorize_width; count++) {
      tmp_r[count] = enif_get_double(env, tmp[count], &t[i + count]);
    }
    int acc = true;
#pragma clang loop vectorize(enable) unroll(enable)
    for (size_t count = 0; count < loop_vectorize_width; count++) {
      acc &= (tmp_r[count] == success);
    }
    if (__builtin_expect((acc == false), false)) {
      enif_free(t);
      return fail;
    }
    i += loop_vectorize_width;
  }
}

int
enif_get_double_vec_from_number_list(ErlNifEnv *env, ERL_NIF_TERM list, double **vec, size_t *vec_l)
{
  ERL_NIF_TERM head, tail;

  if (__builtin_expect((enif_get_list_cell(env, list, &head, &tail) == fail),
                       true)) {
    if (__builtin_expect((enif_is_empty_list(env, list) == success), true)) {
      *vec_l = empty;
      *vec = NULL;
      return success;
    }
    return fail;
  }
  size_t n = init_size_long;
  size_t nn = cache_line_size;
  double *t = (double *)enif_alloc(nn);
  if (__builtin_expect((t == NULL), false)) {
    return fail;
  }

  size_t i = 0;
  while (true) {
    if (__builtin_expect((enif_get_double(env, head, &t[i]) == fail), false)) {
      long tmp;
      if (__builtin_expect((enif_get_int64(env, head, &tmp) == fail), false)) {
        enif_free(t);
        return fail;
      }
      t[i] = (double)tmp;
    }
    i++;
    if (__builtin_expect(
          (enif_get_list_cell(env, tail, &head, &tail) == fail), false)) {
      *vec_l = i;
      *vec = t;
      return success;
    }
    if (__builtin_expect((i >= n), false)) {
      size_t old_nn = nn;
      if (__builtin_expect(((nn & size_t_highest_bit) == 0), true)) {
        nn <<= 1;
        n <<= 1;
      } else {
        nn = size_t_max;
        n = nn / sizeof(long);
      }
      double *new_t = (double *)enif_alloc(nn);
      if (__builtin_expect((new_t == NULL), false)) {
        enif_free(t);
        return fail;
      }
      memcpy(new_t, t, old_nn);
      enif_free(t);
      t = new_t;
    }
  }
}

int
enif_get_2_double_vec_from_number_tuple_list(ErlNifEnv *env, ERL_NIF_TERM list, double **vec1, double **vec2, size_t *vec_l)
{
  ERL_NIF_TERM head, tail;

  if (__builtin_expect((enif_get_list_cell(env, list, &head, &tail) == fail),
                       true)) {
    if (__builtin_expect((enif_is_empty_list(env, list) == success), true)) {
      *vec_l = empty;
      *vec1 = NULL;
      *vec2 = NULL;
      return success;
    }
    return fail;
  }
  size_t n = init_size_long;
  size_t nn = cache_line_size;
  double *t1 = (double *)enif_alloc(nn);
  if (__builtin_expect((t1 == NULL), false)) {
    return fail;
  }
  double *t2 = (double *)enif_alloc(nn);
  if (__builtin_expect((t2 == NULL), false)) {
    return fail;
  }

  size_t i = 0;
  while (true) {
    const ERL_NIF_TERM *tuple;
    int arity;
    if (__builtin_expect((enif_get_tuple(env, head, &arity, &tuple) == fail), false)) {
      enif_free(t1);
      enif_free(t2);
      return fail;
    }
    if(__builtin_expect((arity != 2), false)) {
      enif_free(t1);
      enif_free(t2);
      return fail;
    }
    if (__builtin_expect((enif_get_double(env, tuple[0], &t1[i]) == fail), false)) {
      long tmp;
      if (__builtin_expect((enif_get_int64(env, tuple[0], &tmp) == fail), false)) {
        enif_free(t1);
        enif_free(t2);
        return fail;
      }
      t1[i] = (double)tmp;
    }
    if (__builtin_expect((enif_get_double(env, tuple[1], &t2[i]) == fail), false)) {
      long tmp;
      if (__builtin_expect((enif_get_int64(env, tuple[1], &tmp) == fail), false)) {
        enif_free(t1);
        enif_free(t2);
        return fail;
      }
      t2[i] = (double)tmp;
    }
    i++;
    if (__builtin_expect(
          (enif_get_list_cell(env, tail, &head, &tail) == fail), false)) {
      *vec_l = i;
      *vec1 = t1;
      *vec2 = t2;
      return success;
    }
    if (__builtin_expect((i >= n), false)) {
      size_t old_nn = nn;
      if (__builtin_expect(((nn & size_t_highest_bit) == 0), true)) {
        nn <<= 1;
        n <<= 1;
      } else {
        nn = size_t_max;
        n = nn / sizeof(long);
      }
      double *new_t = (double *)enif_alloc(nn);
      if (__builtin_expect((new_t == NULL), false)) {
        enif_free(t1);
        enif_free(t2);
        return fail;
      }
      memcpy(new_t, t1, old_nn);
      enif_free(t1);
      t1 = new_t;

      new_t = (double *)enif_alloc(nn);
      if (__builtin_expect((new_t == NULL), false)) {
        enif_free(t1);
        enif_free(t2);
        return fail;
      }
      memcpy(new_t, t2, old_nn);
      enif_free(t2);
      t2 = new_t;
    }
  }
}


static ERL_NIF_TERM
map_mult_elem_2(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
  if (__builtin_expect((argc != 1), false)) {
    return enif_make_badarg(env);
  }
  size_t vec_l;
  double *vec_1, *vec_2;
  if (__builtin_expect((enif_get_2_double_vec_from_number_tuple_list(env, argv[0], &vec_1, &vec_2, &vec_l) == fail), false)) {
    return enif_make_badarg(env);
  }
#pragma clang loop vectorize_width(loop_vectorize_width)
  for(size_t i = 0; i < vec_l; i++) {
    vec_1[i] = ((vec_1[i]) * (vec_2[i]));
  }
  return enif_make_list_from_double_vec(env, vec_1, vec_l);
}
static
ErlNifFunc nif_funcs[] =
{
  // {erl_function_name, erl_function_arity, c_function}
  {"map_mult_elem_2", 1, map_mult_elem_2},
};
ERL_NIF_INIT(Elixir.PelemayNifElixirAnnexDataList2D, nif_funcs, &load, &reload, &upgrade, &unload)

zacky1972 avatar Sep 23 '19 01:09 zacky1972