LoginSignup
15
6

More than 3 years have passed since last update.

「Pelemay」EnumコードのElixirネイティブコンパイラをWindowsで試す

Last updated at Posted at 2019-09-26

fukuoka.exのpiacereです
ご覧いただいて、ありがとうございます :bow:

fukuoka.ex発足から半年後にスタートし、今月、満を持してリリースされた、EnumをSIMD命令として生成する(将来的にはGPUをドライブするコードも生成予定)Elixirネイティブコードコンパイラ「Pelemay」をWindowsで試します

1年弱くらい前、この「Enumのみをネイティブコードコンパイルする」「Enum以外は、通常通り、ErlangVMを呼び出す」という棲み分けのコンセプトを @zacky1972 さんとサンドイッチ屋で話し、半年前にテキサスで開催されたLonestar ElixirConf 2019でデビュー&開発スケジュールを公開し、先月のElixirConf US 2019で遂にお披露目となりました

Lonestar ElixirConf 2019 - Hastega: Challenge for GPGPU on Elixir
image.png

ElixirConf US 2019 - Return of Wabi-Sabi: Hastega Will Bring More and More Computational Power to Elixir
image.png

まだ数値の四則計算しかできないですが、先週まで、ElixirConf JP 2019の開催で一杯だった私もようやく落ち着いたので、Pelemayをキャッチアップして、データサイエンスの処理の大半を占める「String.replace」が利用可能なエディションに改善するプルリクとか出したいと思います(Issueは下記の通り、既に出してます)
https://github.com/zeam-vm/pelemay/issues/49

本コラムの検証環境、事前構築のコマンド

本コラムは、以下環境で検証しています(Windowsで実施していますが、Linuxやmacでも動作する想定です)

Pelemayを使うPJを作成

実際にPelemayを使うPJを作る過程で説明します

こちらのチュートリアルを参考に、予めElixirはインストールしておいてください

なお、Pelemayは、Elixir 1.9以上しかサポートしないため、1.8以下の方は、Elixirのアップグレードをお忘れなく

適当なフォルダ配下で、Elixir PJを作成します

mix new pelemay_sample
cd pelemay_sample

mix.exsのdepsに、Pelemayを追加します

PelemayのGithubに記載ある通り、バージョン指定では無く、masterから拾います

mix.exs
defmodule PelemaySample.MixProject do

  defp deps do
    [
      { :pelemay, git: "https://github.com/zeam-vm/pelemay.git", branch: "master" },
    ]

ライブラリ取得します

mix deps.get

まず、ライブラリ配下のPelemayのテストを実行します

cd deps/pelemay
mix test
...................

Finished in 0.2 seconds
8 doctests, 11 tests, 0 failures

Randomized with seed 142000

計19件のテストが通りました…コード内の「## Examples」の数よりも、doctestが少ない気がする…

ひとまず置いといて(笑)、先に進みます

Pelemayサンプルコードを実行する

PelemayのGithubに書いてあるサンプルコードを、作ったPJのモジュールに書いてみます

lib/pelemay_sample.ex
defmodule PelemaySample do
  require Pelemay
  import Pelemay

  defpelemay do
    def map_square (list) do
      list
      |> Enum.map(& &1 * &1)
    end
  end
end

コンパイルしてみましょう

cd ../..
mix compile
==> pelemay
Compiling 8 files (.ex)
Generated pelemay app
==> pelemay_sample
Compiling 1 file (.ex)
clang -Ofast -g -ansi -pedantic -femit-all-decls -Ic:/Tools/erl9.0/erts-9.0/include -I/usr/local/include -I/usr/include -L/usr/local/lib -L/usr/lib -std=c11 -Wno-unused-function -shared -o c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.so c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.c
In file included from c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.c:3:
In file included from c:/Tools/erl9.0/erts-9.0/include\erl_nif.h:31:
c:/Tools/erl9.0/erts-9.0/include/erl_drv_nif.h:105:18: warning: extension used [-Wlanguage-extension-token]
typedef unsigned __int64 ErlNapiUInt64;
                 ^
c:/Tools/erl9.0/erts-9.0/include/erl_drv_nif.h:106:16: warning: extension used [-Wlanguage-extension-token]
typedef signed __int64 ErlNapiSInt64;
               ^
c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.c:106:50: warning: incompatible pointer types passing 'long *' to parameter of type 'ErlNifSInt64 *'
      (aka 'long long *') [-Wincompatible-pointer-types]
          tmp_r[c] = enif_get_int64(env, tmp[c], &t[i++]);
                                                 ^~~~~~~
c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.c:147:54: warning: incompatible pointer types passing 'long *' to parameter of type 'ErlNifSInt64 *'
      (aka 'long long *') [-Wincompatible-pointer-types]
      tmp_r[count] = enif_get_int64(env, tmp[count], &t[i + count]);
                                                     ^~~~~~~~~~~~~
c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.c:274:55: warning: incompatible pointer types passing 'long *' to parameter of type 'ErlNifSInt64 *'
      (aka 'long long *') [-Wincompatible-pointer-types]
      if (__builtin_expect((enif_get_int64(env, head, &tmp) == fail), false)) {
                                                      ^~~~
5 warnings generated.

== Compilation error in file lib/pelemay_sample.ex ==
** (ArgumentError) argument error
    (stdlib) :io.put_chars(:standard_io, :unicode, [<<32, 32, 32, 131, 137, 131, 67, 131, 117, 131, 137, 131, 138, 32, 99, 58, 47, 112, 105, 97, 99, 101, 114, 101, 47, 99, 111, 100, 101, 47, 112, 101, 108, 101, 109, 97, 121, 95, 115, 97, 109, 112, 108, 101, 47, 95, 98, 117, 105, ...>>, 10])
    lib/pelemay.ex:53: Pelemay.pelemaystub/2
    expanding macro: Pelemay.defpelemay/1
    lib/pelemay_sample.ex:5: PelemaySample (module)

おや?なんか盛大にエラーが出てますね…

どうやら、IO.putsのネイティブコード(:io.put_chars)のUnicode周りで問題ありの模様

ん?これはWindowsのターミナルのせいか?

chcp 65001

改めてコンパイルします

mix compile
Compiling 1 file (.ex)
clang -Ofast -g -ansi -pedantic -femit-all-decls -Ic:/Tools/erl9.0/erts-9.0/include -I/usr/local/include -I/usr/include -L/usr/local/lib -L/usr/lib -std=c11 -Wno-unused-function -shared -o c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.so c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.c
In file included from c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.c:3:
In file included from c:/Tools/erl9.0/erts-9.0/include\erl_nif.h:31:
c:/Tools/erl9.0/erts-9.0/include/erl_drv_nif.h:105:18: warning: extension used [-Wlanguage-extension-token]
typedef unsigned __int64 ErlNapiUInt64;
                 ^
c:/Tools/erl9.0/erts-9.0/include/erl_drv_nif.h:106:16: warning: extension used [-Wlanguage-extension-token]
typedef signed __int64 ErlNapiSInt64;
               ^
c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.c:106:50: warning: incompatible pointer types passing 'long *' to parameter of type 'ErlNifSInt64 *'
      (aka 'long long *') [-Wincompatible-pointer-types]
          tmp_r[c] = enif_get_int64(env, tmp[c], &t[i++]);
                                                 ^~~~~~~
c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.c:147:54: warning: incompatible pointer types passing 'long *' to parameter of type 'ErlNifSInt64 *'
      (aka 'long long *') [-Wincompatible-pointer-types]
      tmp_r[count] = enif_get_int64(env, tmp[count], &t[i + count]);
                                                     ^~~~~~~~~~~~~
c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.c:274:55: warning: incompatible pointer types passing 'long *' to parameter of type 'ErlNifSInt64 *'
      (aka 'long long *') [-Wincompatible-pointer-types]
      if (__builtin_expect((enif_get_int64(env, head, &tmp) == fail), false)) {
                                                      ^~~~
5 warnings generated.

== Compilation error in file lib/pelemay_sample.ex ==
** (ArgumentError) argument error
    (stdlib) :io.put_chars(:standard_io, :unicode, [<<32, 32, 32, 131, 137, 131, 67, 131, 117, 131, 137, 131, 138, 32, 99, 58, 47, 112, 105, 97, 99, 101, 114, 101, 47, 99, 111, 100, 101, 47, 112, 101, 108, 101, 109, 97, 121, 95, 115, 97, 109, 112, 108, 101, 47, 95, 98, 117, 105, ...>>, 10])
    lib/pelemay.ex:53: Pelemay.pelemaystub/2
    expanding macro: Pelemay.defpelemay/1
    lib/pelemay_sample.ex:5: PelemaySample (module)

ううむ、同じ箇所のエラーは解消しない…

ソースコードの5行目を見ると、defpelemayマクロで、エラーメッセージを見る限り、その中のpelemaystubでIO.putsしててコケているようなので、元のソースコードを見てみます

deps/pelemay/lib/pelemay.ex
defmodule Pelemay do

  defp pelemaystub(ret, module) do
    Generator.generate(module)
    ret
  end

deps/pelemay/lib/pelemay/generator.ex
defmodule Pelemay.Generator do

  def generate(module) do
    Application.app_dir(:pelemay, "priv")
    |> File.mkdir()

    Interface.generate(module)
    Native.generate(module)
    Builder.generate(module)
  end

エラーメッセージから追えるのは、ここまでなので、grepでIO.putsを追うと、Builderモジュールが該当するようなので見てみます

defmodule Pelemay.Generator.Builder do

  def generate(module) do
    
    options =
      cflags ++ ["-shared"] ++ ldflags ++ ["-o", Generator.libso(module), Generator.libc(module)]

    IO.puts(Enum.join([@cc] ++ options, " "))

    {result, 0} = System.cmd(@cc, options)

    IO.puts(result)
  end

この2つのいずれか、もしくは両方と思われるので、コメントアウトしてみます

defmodule Pelemay.Generator.Builder do

  def generate(module) do
    
    options =
      cflags ++ ["-shared"] ++ ldflags ++ ["-o", Generator.libso(module), Generator.libc(module)]

#    IO.puts(Enum.join([@cc] ++ options, " "))

    {result, 0} = System.cmd(@cc, options)

#    IO.puts(result)
  end

depsを再コンパイルします

mix deps.compile

本体をコンパイルします

mix compile
Compiling 1 file (.ex)
In file included from c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.c:3:
In file included from c:/Tools/erl9.0/erts-9.0/include\erl_nif.h:31:
c:/Tools/erl9.0/erts-9.0/include/erl_drv_nif.h:105:18: warning: extension used [-Wlanguage-extension-token]
typedef unsigned __int64 ErlNapiUInt64;
                 ^
c:/Tools/erl9.0/erts-9.0/include/erl_drv_nif.h:106:16: warning: extension used [-Wlanguage-extension-token]
typedef signed __int64 ErlNapiSInt64;
               ^
c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.c:106:50: warning: incompatible pointer types passing 'long *' to parameter of type 'ErlNifSInt64 *'
      (aka 'long long *') [-Wincompatible-pointer-types]
          tmp_r[c] = enif_get_int64(env, tmp[c], &t[i++]);
                                                 ^~~~~~~
c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.c:147:54: warning: incompatible pointer types passing 'long *' to parameter of type 'ErlNifSInt64 *'
      (aka 'long long *') [-Wincompatible-pointer-types]
      tmp_r[count] = enif_get_int64(env, tmp[count], &t[i + count]);
                                                     ^~~~~~~~~~~~~
c:/piacere/code/pelemay_sample/_build/dev/lib/pelemay/priv/libnifelixirpelemaysample.c:274:55: warning: incompatible pointer types passing 'long *' to parameter of type 'ErlNifSInt64 *'
      (aka 'long long *') [-Wincompatible-pointer-types]
      if (__builtin_expect((enif_get_int64(env, head, &tmp) == fail), false)) {
                                                      ^~~~
5 warnings generated.
Generated pelemay_sample app

ひとまずコンパイルは通ったので、実行しています

iex -S mix
iex> PelemaySample.map_square [ 1, 2, 3 ]
** (UndefinedFunctionError) function PelemayNifElixirPelemaySample.map_mult/1 is undefined (module PelemayNifElixirPelemaySample is not available)
    (pelemay_sample) PelemayNifElixirPelemaySample.map_mult([1, 2, 3])
iex(1)>
23:18:07.913 [warn]  The on_load function for module Elixir.PelemayNifElixirPelemaySample returned:
{:error, {:load_failed, [70, 97, 105, 108, 101, 100, ...]}}

うーむ、今度は、PelemayNifElixirPelemaySample.map_multが存在しない…

findすると、BEAM自体は存在しているので、関数が未定義なんでしょうね…

ここから先は、ネイティブコード生成周り含めた深追いする必要もありそうなので、今回は一旦ここまでにしておきましょう

2019/9/27続報

いったん暫定で動きました

_build/dev/lib/pelemay/priv/配下にある、libnifelixirpelemaysample.soというファイルを、libnifelixirpelemaysample.dllにリネームするだけです

リネーム後に動かすと、こうなります

iex -S mix
iex> PelemaySample.map_square [ 1, 2, 3 ]
[1, 4, 9]

Pelemayの、Windowsビルド時のLLVMオプション指定を修正ですかねー

終わり

ひとまず、WindowsでのPelemay初体験は、ランタイムエラーで一時断念 … という結果でした

macでは、特に滞り無く、IO.putsでのエラーも出ず、実行できているようなので、きっとWindows固有の問題でしょう

@zacky1972 さんや、 @hisaway さんと、お話しながら、解決を進めたいと思います

(2019/9/27続報)
ひとまず、WindowsでのPelemay初体験は、暫定的ではありますが、うまくいきました

下記Bug reportを挙げました
https://github.com/zeam-vm/pelemay/issues/52

あと、コラム中で挙げたdoctest不具合についても、下記Bug reportが作られていました
https://github.com/zeam-vm/pelemay/issues/50
https://github.com/zeam-vm/pelemay/issues/51

いったんは、Pelemayを手元のWindowsで動かすことができたので、今度は、データサイエンスの処理の大半を占める「String.replace」を利用可能にしていきたいと思います

p.s.「いいね」よろしくお願いします

ページ左上の image.pngimage.png のクリックを、どうぞよろしくお願いします:bow:
ここの数字が増えると、書き手としては「ウケている」という感覚が得られ、連載を更に進化させていくモチベーションになりますので、もっとElixirネタを見たいというあなた、私達と一緒に盛り上げてください!:tada:

15
6
18

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
6