(この記事は「数値計算 Advent Calendar 2018」の10日目です)
「数値計算 Advent Calendar 2018」9日目は @i153 さんの「ピタゴラス3体問題で遊ぶ」でした。
「数値計算 Advent Calendar 2018」10日目の今日は,今までの記事とは違った観点の記事を書きたいと思います。
数値計算を実際に行うプログラミング言語として,みなさんは何をお使いでしょうか? Python? R? MATLAB? Julia? Fortran? 他にも選択肢はあると思います。
今回ご紹介するのは Elixir (エリクサー)という並列プログラミング言語です。数値計算に使えるライブラリを研究開発していきますので,注目していただけると幸いです。
Elixir の特長
Elixir は並列プログラミングに長けています。
Elixir は並列プログラミングに長けたプログラミング言語です。例えば次のようなコードをご覧ください。
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, ...]
を生成します。
このような**MapReduceによる記述は並列処理にとても向いています。**この例だと,リストの1つ1つの要素に対して,関数 foo
と bar
を順番に適用するわけですが,それぞれの要素に対する計算は互いに干渉しません。そのため,各要素の計算を異なるコアに配置して独立させて計算しても結果は変わらないですし,互いにコミュニケーションを取り合いながら同期させて計算する必要もありません。
このことを利用して,Elixir では Flow という並列処理ライブラリが開発されてきました。 Enum.map
のときとコードはとても似ています。
1..1_000_000
|> Flow.from_enumerable() # リストの要素を各コアに分配する
|> Flow.map(foo)
|> Flow.map(bar)
|> Enum.to_list() # リストに戻す
このように書くだけで,並列処理をしてくれます。
こうした Elixir の特長を生かし,Phoenix というウェブフレームワークも開発されました。Phoenixはウェブフレームワークとしては最も高いレベルのレスポンス性能を有しています。
余談ですが,Elixir も Phoenix もファイナルファンタジー由来の名称です。
ベクトル計算/行列計算
ベクトル計算/行列計算を行う Elixir のライブラリはいくつかあります。
私たち fukuoka.ex で @hisaway さんが研究開発した事例をご紹介します。
ベクトル計算
|> Elixir + Rustlerでベクトル演算を高速化しよう〜Rustler初級者編 1 〜
|> Elixir + Rustlerでベクトル演算を高速化しよう 〜初級者編 1.5〜
線形回帰
@hisaway さんが研究開発した事例をご紹介します。
線形回帰
|> RustElixirで線形回帰を高速化した話
現状としては,Rust を用いて SIMD 並列命令を含むネイティブコードにコンパイルしたことによって高速化を図ることには成功しました。
しかし,rayon によるマルチコア並列化をすると遅くなりました。これは,マルチコア並列化に伴う各コアへの配分と集計にかかるコストに見合うほどの大きさのデータ量ではなかったことによるものと考察しました。現在,データ量を増やす実験を試みているところです。
また,GPU駆動による超並列化にも取り組んでいるところです。
ニューラルネット
「fukuoka.ex Elixir/Phoenix Advent Calendar 2018」10日目に @curry_on_a_rice さんが「ニューラルネットを実装した話」を発表予定です。お楽しみに!
CPU/GPU超並列処理系 Hastega (ヘイスガ)
Hastega(ヘイスガ)の名称はファイナルファンタジーに登場する最強のスピードアップ呪文に由来します。ちなみに Elixir や Phoenix もファイナルファンタジー由来の名称です。この研究プロジェクトが目標とするマルチコア CPU / GPU をフル活用して高速化する技術として Hastega は最もふさわしい名称ではないでしょうか。
Hastega の原理としては,Elixir の前述のコード
1..1_000_000
|> Enum.map(foo)
|> Enum.map(bar)
は,単純で均質で大量にあるデータ 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倍以上となりました。発表資料(論文,プレゼンテーション,ポスター)を下記に示します。
その後,複数の研究助成を受けて数々のマシンでテストする機会が得られたり,研究室学生が研究に合流してくれたりして,研究が進みました。
今度,Lonestar ElixirConf 2019 (2月28日〜3月2日 テキサス州 オースティン)でも発表してきます!
前述のベクタ/行列計算や線形回帰,ニューラルネットについても,Hastega を用いて Elixir コードから SIMD 命令や GPU を用いた並列ネイティブコードにコンパイルできるようにする予定です。C言語や Julia に匹敵する高速性能を実現できればと考えています。
今後の展望: DSLとトランスレータの構想
数値計算については,Python, R, MATLAB, Julia, Fortran などの既存言語に膨大な資産と経験知があります。既存言語の資産と経験知を活用できるように,Elixir ベースの数値計算 DSL (ドメイン特化言語) ライブラリと,既存言語からこの DSL に変換するトランスレータを研究開発しようと考えています。もちろんこの DSL で書くと Hastega による高速アクセラレーションが働くようにします。
Elixir を使ってみたくなったら
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(更新編)
データサイエンスシリーズ
|> 関数型でデータサイエンス#1:様々なデータをインプットする
|> 関数型でデータサイエンス#2:インプットしたデータを変換する
|> 関数型でデータサイエンス番外編:様々な日時文字列を扱えるようにする
|> 関数型でデータサイエンス#3:インプットしたデータを集約する①
|> 関数型でデータサイエンス#4:インプットしたデータを集約する②
おわりに
「数値計算 Advent Calendar 2018」10日目の今日は,今までの記事とは違った観点の記事として,Elixir を用いて数値計算をできるようにするための研究開発事例をご紹介しましたが,いかがだったでしょうか? 今後,数値計算分野でも Elixir にご注目ください。
次にアドベントカレンダー記事を書くのは2018年12月15日「fukuoka.ex Elixir/Phoenix Advent Calendar 2018」の15日目「ZEAM開発ログ2018年総集編その2: Elixir 研究構想についてふりかえる(後編)」です。お楽しみに!
「数値計算 Advent Calendar 2018」11日目は @dif_engine さんです。こちらもお楽しみに!