Help us understand the problem. What is going on with this article?

ElixirとRustをつなぐRustlerを使った事例紹介

More than 1 year has passed since last update.

(この記事は「Rust Advent Calendar 2018」の8日目です)

「Rust Advent Calendar 2018」7日目は, @blackenedgold (κeen) さんの「Rustのモジュールの使い方 2018 Edition版」でした。

「Rust Advent Calendar 2018」8日目の今日は,ElixirとRustをつなぐRustlerというライブラリについてと,私たちが開発したRustlerの事例を紹介したいと思います。

背景

Rust 推しのみなさんの前で喧嘩を売るようで恐縮ですが,私たち fukuoka.exElixir(エリクサー) 推しです。なぜ Elixir に注目しているかというと,Elixir の持つ並列処理性能耐障害性が高い上に,文法が平易で記述が容易であるから,そして Phoenix(フェニックス)という最速のウェブフレームワークを持つからです。

しかしながら,現状の Elixir の実行時環境である Erlang VM (アーラン ブイエム) は,Rust や C/C++ などのネイティブコードにコンパイルするプログラミング言語処理系と比べて低速です。

そこで,Elixir (というか Erlang VM) には,NIF(Native Implemented Function)というネイティブコード実行に関わるAPIが整備されています。NIFは通常C/C++で書くのですが,Elixir では Rustler (ラスラー)というライブラリによって NIF を記述するプログラミング言語として Rust を利用することができるようになります。

Rustler によって Elixir と Rust は共存共栄の道を辿ることができます!

そこで本記事では,Rustler と私たち fukuoka.ex が開発した Rustler の事例についてご紹介したいと思います。最後に,今後私たち fukuoka.ex が考えている展望について述べたいと思います。

Rustler の事例紹介

私たち fukuoka.ex では,主に @twinbee (enぺだーし)さんと,私 @zacky1972,そして新進気鋭の @hisaway さんが Rustler を使った事例を開発し,記事やソースコードを公開しています。次のような感じです。

@twinbee (enぺだーし)さんの事例
|> Elixirから簡単にRustを呼び出せるRustler #4 SHIFT-JIS変換を行う
|> Elixirで一千万行のJSONデータで遊んでみた Rustler編
|> mbcs_rs

@zacky1972 の事例
|> ロジスティック写像ベンチマーク
|> Hastega
|> micro Elixir / ZEAM

@hisaway さんの事例
|> ベクトル計算
|> 線形回帰

今回は次の事例について詳しく紹介します。

  • 各種文字コード変換(mbcs_rs, ついでにMojiex)
  • ベクトル計算 / 線形回帰
  • Hastega
  • micro Elixir / ZEAM

各種文字コード変換(mbcs_rs, ついでにMojiex)

@twinbee (enぺだーし)さんが開発した事例です。

mbcs_rsは,エンコーディングを変換します。

下記のようにすると,"日本語"という文字列をシフトJISに変換します。

"日本語" |> MbcsRs.encode!("SJIS")

下記のようにすると,"日本語"という文字列をシフトJISに変換した後,元の文字列(UTF-8)に戻します。

"日本語" |> MbcsRs.encode!("SJIS") |> MbcsRs.decode!("SJIS")

ちなみに Rust を使った事例ではありませんが enぺだーしさん作の Mojiexは,全角半角変換をします。

たとえば下記のようにすると半角カナを全角カナに変換して,"ABCD 01234あいうあいうABCD 01234あいう" という結果を得ます。

"ABCD 01234あいうアイウABCD 01234アイウ" |> Mojiex.convert({:hk, :zk})

ベクトル計算

@hisaway さんが研究開発した事例です。

ベクトル計算
|> Elixir + Rustlerでベクトル演算を高速化しよう〜Rustler初級者編 1 〜
|> Elixir + Rustlerでベクトル演算を高速化しよう 〜初級者編 1.5〜

線形回帰
|> RustElixirで線形回帰を高速化した話

Hastega

@zacky1972 が研究開発している事例です。

Hastega(ヘイスガ)の名称はファイナルファンタジーに登場する最強のスピードアップ呪文に由来します。ちなみに Elixir や Phoenix もファイナルファンタジー由来の名称です。この研究プロジェクトが目標とするマルチコア CPU / GPU をフル活用して高速化する技術として Hastega は最もふさわしい名称ではないでしょうか。

Elixir では MapReduce に基づくプログラミングスタイルが広く普及しています。例えば次のようなコードです。

1..1_000_000
|> Enum.map(foo)
|> Enum.map(bar)
  • 1行目の1..1_000_000は,1から1,000,000までの要素からなるリストを生成します。なお,数字の間の_(アンダースコア)によって,数字を分割するコンマを表します。
  • 2,3行目の先頭にある|>パイプライン演算子で,パイプライン演算子の前に書かれている記述の値を,パイプライン演算子の後に書かれた関数の第1引数として渡します。すなわち,このような記述と等価です。Enum.map(Enum.map(1..1_000_000, foo), bar)
  • 2,3行目に書かれている Enum.mapは,第1引数に渡されるリスト(など)の要素1つ1つに,第2引数で渡される関数を適用します。ここでは関数 foo を各要素に適用した後,関数 bar を各要素に適用します。もし,fooが2倍する関数で,barが1加える関数だった時には,これらの記述により,2倍してから1加える処理を1から1,000,000までの要素に適用したリスト,[3, 5, 7, ...] を生成します。

Hastega の原理としては,上記のコードは,単純で均質で大量にあるデータ 1..1_000_000同じような命令列 foo |> bar で構成されます。したがって,これはGPUの基本アーキテクチャであるSIMDに適合します。次のようにOpenCLのネイティブコードで書いてみると,先ほどのコードから容易に変換できそうです。

__kernel void calc(
  __global long* input,
  __global long* output) {
  size_t i = get_global_id(0);
  long temp = input[i];
  temp = foo(temp);
  temp = bar(temp);
  output[i] = temp;
}

このアイデアに基づいてプロトタイプを実装してみたところ,Python の NumPy ライクな GPU 実行系である CuPy での実行と比べて,3倍以上速度向上するという結果が得られました。

Hastega には SIMD 命令を用いた並列実行機能も備えようとしています。現時点では Rust によるループに対する SIMD 命令生成をそのまま利用しています。

整数演算ベンチマークについて,Elixir から,SIMD 命令を用いたマルチコアCPU駆動(rayon crate) のネイティブコードおよび OpenCL (ocl crate) による GPU 駆動のネイティブコードを呼び出す Hastega プロトタイプを開発しました。8月にプログラミング研究会とSWESTにて発表しました。当時得られた結果では Elixir からの速度向上は約4〜8倍,Pythonからの速度向上は3倍以上となりました。発表資料(論文,プレゼンテーション,ポスター)を下記に示します。

Hastega: Elixirプログラミングにおける超並列化を実現するためのGPGPU活用手法

Hastega: Elixirプログラミングにおける超並列化を実現するためのGPGPU活用手法

Hastega: Elixirプログラミングにおける超並列化を実現するためのGPGPU活用手法

その後,複数の研究助成を受けて数々のマシンでテストする機会が得られたり,研究室学生が研究に合流してくれたりして,研究が進みました。

今度,Lonestar ElixirConf 2019 (2月28日〜3月2日 テキサス州 オースティン)でも発表してきます!

Presentation at Lonestar ElixirConf 2019

micro Elixir / ZEAM

ZEAM(ジーム) は ZACKY's Elixir Abstract Machine の略です。Erlang VM の BEAM (Bogdan/Björn's Erlang Abstract Machine)に対応するような形で命名しました。ZEAM という名称の初出は2018年2月の「fukuoka.ex #5」です。

fukuoka.ex ZEAM開発ログ 第1回: BEAMバイトコード・インサイド〜30年の歴史を誇るBEAMを超えるには

ZEAM はその名の通り,Erlang VM に代わる Elixir ネイティブな処理系として構想されました。当初構想では BEAM バイトコードと互換性を持たせるつもりでいたのですが,BEAMバイトコードの解析に難儀したことと,その後の議論で,バイトコードレベルの互換性は不要で,Elixir のソースコードレベルの互換性があれば良いという結論に至り,当初構想から大きく方向転換することとなりました。

現在の構想では,Elixir のサブセットとなるプログラミング言語を策定し,その言語をコンパイル・実行する処理系として研究開発を始動しています。このサブセット言語を micro Elixir と呼んでいます。

micro Elixir / ZEAM 構想の初出はfukuoka.ex#13:夏のfukuoka.ex祭=技術のパラダイムシフトおよびSWEST20です。下記のプレゼンテーションの後半で示されるように,かなり野心的な構想になっています。

耐障害性が高くマルチコア性能を最大限発揮できるElixir(エリクサー)を学んでみよう

micro Elixir の全ての仕様はまだ確定していませんが,まずは Elixir のデータ処理の部分を抜き出して Hastega のコードを生成するという部分に集中することにしました。また,当面は NIFコードを生成することとし,Elixir / Erlang VM から呼び出すようにすることにしました。これを Hastega / micro Elixir / ZEAM と呼んでいます。このようにデザインすることで,すぐに既存のElixirのコードに組込むことが可能になります。

micro Elixir / ZEAM を開発するには,Elixir コードを解析して中間コードにする部分(解析部)と,中間コードを元に最適化・コード生成する部分(合成部)を開発する必要があります。

私たちが採用している基本構成は次のようにします。

  • 解析部: Elixir の言語処理系をそのまま利用し Elixir マクロを用いて拡張する
  • 合成部: 近年のコンパイラで広く普及している LLVM を用いて,Elixir からコードを生成・実行できるようにする

それぞれを採用した理由は次の通りです。

  • Elixirマクロは,簡潔な記述ながらとても強力な機能を有しています。また,既存の Elixir の文法であれば,とくに容易に記述できます。今回は,Elixir の文法を元に処理系を開発することから,Elixir マクロの採用により,開発効率が向上します。
  • LLVMは,対応しているプロセッサが多く,最適化も充実しています。したがって LLVM のコードを生成することで,幅広いアーキテクチャに最適なコードを生成できる可能性が高まります。

今のところ,LLVM については,Elixir から Rustler 経由で Rust の LLVM バイディングである llvm-sys につなげて出力しています。

Hastega / micro Elixir / ZEAM の詳細

2018年のアドベントカレンダーに Hastega / micro Elixir / ZEAM を特集しています。

Hastega / micro Elixir / ZEAM
|> ZEAM開発ログ2018年総集編その1: Elixir 研究構想についてふりかえる(前編)
|> ZEAM開発ログ: Elixir マクロ + LLVM で超並列プログラミング処理系を研究開発中

今までの研究開発記録は下記目次から参照ください。

ZEAM開発ログ 目次

今後の展望

今回ご紹介した Rustler の最後の事例である micro Elixir / ZEAM ですが,完成すると Elixir コードから LLVM でネイティブコードを生成して実行できるようになります。すなわち,Rustの最大のライバルを私たち fukuoka.ex は作ろうとしているわけです...!!

しかしながら,もしそうなったとしても,micro Elixir / ZEAM の心臓部は,依然として Rust で記述する予定です。また,micro Elixir / ZEAM の研究開発に伴い,Rust の crate もいろいろ開発することでしょう。

また Elixir と Rust の得意分野は異なります。Elixir の強みは並列処理を簡単に扱えることやウェブインタフェースなどです。一方,Rust は堅牢な低レベルプログラミングシステムを構築するのに長けています。micro Elixir / ZEAM や Rustler を介して連携させることで,お互いの強みを発揮できます!

したがって,micro Elixir / ZEAM と Rustler によって Elixir と Rust は共存共栄の道を辿ることができます!

Rustler や Elixir を使ってみたくなったら

Rustler 入門記事
|> Elixirから簡単にRustを呼び出せるRustler #1 準備編
|> Elixirから簡単にRustを呼び出せるRustler #2 クレートを使ってみる
|> Elixirから簡単にRustを呼び出せるRustler #3 いろいろな型を呼び出す
|> Elixirから簡単にRustを呼び出せるRustler #4 SHIFT-JIS変換を行う
|> Elixirから簡単にRustを呼び出せるRustler #5 NIFからメッセージを返す

|> Elixir-NIF-Rustボイラープレート Ruster0.17.1の注意点

Elixir 入門記事
|> Excelから関数型言語マスター1回目:行の「並べ替え」と「絞り込み」
|> Excelから関数型言語マスター2回目:「列の抽出」と「Web表示」
|> Excelから関数型言語マスター3回目:WebにDBデータ表示【PostgreSQL or MySQL編】
|> Excelから関数型言語マスター4回目:Webに外部APIデータ表示
|> Excelから関数型言語マスター5回目:Webにグラフ表示
|> Excelから関数型言語マスター6回目: Vue.js+内部API(表示編)
|> Excelから関数型言語マスター7回目: Vue.js+内部API(更新編)

おわりに

今回は Elixir と Rust をつなぐかけはしとなる Rustler について,事例とともに紹介しましたが,いかがだったでしょうか? ぜひこの機会に Elixir に触れてみてください!

次にアドベントカレンダーの記事を書くのは,明日12/9公開予定の「機械学習工学 / MLSE Advent Calendar 2018」9日目の「並列プログラミング言語 Elixir (エリクサー)を用いた機械学習ツールチェーン」です。お楽しみに!

「Rust Advent Calendar 2018」9日目の明日は @termoshtt さんです。こちらもお楽しみに!

zacky1972
北九州市立大学 国際環境工学部 准教授 / ナッジ社会実装研究センター センター長 / Elixir 推し / fukuoka.ex / Pelemay / ZEAM / Personal Vision Co-Creator / KK-SHiFT / 技術相談,共同研究依頼,進路相談,適職診断など,随時受付ます
https://zacky1972.github.io
fukuokaex
エンジニア/企業向けにElixirプロダクト開発・SI案件開発を支援する福岡のコミュニティ
https://fukuokaex.fun/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away