fukuoka.exのpiacereです
ご覧いただいて、ありがとうございます
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
ElixirConf US 2019 - Return of Wabi-Sabi: Hastega Will Bring More and More Computational Power to Elixir
まだ数値の四則計算しかできないですが、先週まで、ElixirConf JP 2019の開催で一杯だった私もようやく落ち着いたので、Pelemayをキャッチアップして、データサイエンスの処理の大半を占める「String.replace」が利用可能なエディションに改善するプルリクとか出したいと思います(Issueは下記の通り、既に出してます)
https://github.com/zeam-vm/pelemay/issues/49
本コラムの検証環境、事前構築のコマンド
本コラムは、以下環境で検証しています(Windowsで実施していますが、Linuxやmacでも動作する想定です)
- Windows 10
- Elixir 1.9.1 ※最新版のインストール手順はコチラ
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から拾います
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のモジュールに書いてみます
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しててコケているようなので、元のソースコードを見てみます
defmodule Pelemay do
…
defp pelemaystub(ret, module) do
Generator.generate(module)
ret
end
…
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.「いいね」よろしくお願いします
ページ左上の や のクリックを、どうぞよろしくお願いします
ここの数字が増えると、書き手としては「ウケている」という感覚が得られ、連載を更に進化させていくモチベーションになりますので、もっとElixirネタを見たいというあなた、私達と一緒に盛り上げてください!