Upload
gos-k
View
358
Download
1
Embed Size (px)
Citation preview
Common LispでGPGPU
関東GPGPU勉強会 #42016-08-21
gos-k
自己紹介
● 名前 : gos-k
– gos_k : twitter, bitbucket, qiita
– gos-k : github, hatenablog
● 仕事 : 渋谷の某社でWebプログラマ
– Common LispとかReact.jsとか
– それ以前は産業用機器のソフトや3D画像処理のチューニングの仕事
● 趣味 : パンケーキ
発表の流れ
1) Common Lispに関する簡単な説明
2) Common LispにおけるGPGPU関連の現状
3) Common LispでOpenCL
4) Common Lispでカーネルコードのマクロ展開
今流行りのLisp方言
● Clojure
– JVMで動くヤツ
● Common Lisp
– Lisp方言を寄せ集めたヤツ
● Scheme
– 関数型のヤツ
● Emacs Lisp
– Emacsで動くヤツ
今回はCommon Lispを扱う
Common Lispとは
● 1980年代に数々のLispマシンやLisp方言が生まれた
● コミュニティ分裂の懸念を解消すべく、Common Lispとして標準化を行う
– 既存のLispから最良の機能を集結
– ANSIから1996年に標準規格リリース
● 制御構造、再帰関数呼び出し、ガベージコレクション、インクリメンタルコンパイル、オブジェクトシステム、コンディションシステム等々を持つ
参考文献 : Peter, 実践 Common Lisp, 2008
関連用語
● SBCL (Steel Bank Common Lisp)
– Common Lispの処理系
– 色々あるけど非商用の処理系ならとりあえずコレ
● Quicklisp
– Common Lispのパッケージ管理ツール
– pipとかgemとかnpmとかの仲間?
● Roswell
– 処理系管理ツール?
– デフォルトでSBCLとQuicklispが入って、直ぐに使い始められる
発表の流れ
1) Common Lispに関する簡単な説明
2) Common LispにおけるGPGPU関連の現状
3) Common LispでOpenCL
4) Common Lispでカーネルコードのマクロ展開
GPGPU対応状況
● CUDA
– takagi/cl-cuda (github)
● OpenCL API
– 3b/cl-opencl-3b (github)
– guicho271828/eazy-opencl (github)
– gos-k/cl-oclapi (github)
● OpenCL C
– gos-k/oclcl (github)
CUDAもOpenCLも、ホストもデバイスも、Common Lispで書ける
発表の流れ
1) Common Lispに関する簡単な説明
2) Common LispにおけるGPGPU関連の現状
3) Common LispでOpenCL
4) Common Lispでカーネルコードのマクロ展開
cl-oclapi
● 3bさんのOpenCL APIは動くけど
– Quicklisp対応してない
– 何年もメンテされてない?
● OpenCL 1.2 APIのラッパーをスクラッチから作った
– 数年だれもやってなかったネタなのに、guichoさんと数週間差でネタかぶり
– しかし、その他色々なライブラリの都合により、cl-oclapiがQuicklispのGPGPU関連で一番最初に登録される
oclcl
● cl-cudaからフォークして、カーネル部分取り出し
– S式からCを生成
– マクロのサポート
– シンボルマクロのサポート
● 変更点
– キーワードの置換や組込み関数の拡充
● __global__を__kernelにしたり__synchronizeをbarrierにしたり
– スカラ型ベクタ型のサポート拡大
– テストコードのOpenCL APIライブラリをeazy-openclとcl-oclapiに
– Quicklisp登録済み
発表の流れ
1) Common Lispに関する簡単な説明
2) Common LispにおけるGPGPU関連の現状
3) Common LispでOpenCL
4) Common Lispでカーネルコードのマクロ展開
Common Lispで?
● Cでカーネル書きたくない
– 不便だから
– カーネルの言語は規格上はCのみ
– その他言語はAPI対応ばかりで、カーネルまで書けるのはあまりない
● 自分で作れるか
– Common Lispなら既にcl-cudaがある
● これをベースにすればOpenCLも出来るのでは
– マクロ使えばカーネル最適化に便利そう
Common Lispで?
● マクロで変数が使える
● マクロで関数が使える
● マクロで条件分岐が使える
● マクロでループが使える
● マクロでプリント文が使える
● マクロを一段階展開したものが得られる
マクロで普通にプログラミング出来る
例 : unroll
(defkernelmacro unroll ((n offset) &body body) `(progn ,@(loop for i below n collect `(symbol-macrolet ((,offset ,i)) ,@body))))
(defkernel test-unroll (void ((input-buffer float*) (output-buffer float*))) (let ((offset (* 4 (to-int (get-global-id 0))))) (unroll (4 i) (set (aref output-buffer (+ offset i)) (aref input-buffer (+ offset i))))))
int offset = (4 * (int)(get_global_id(0)));output_buffer[(offset + 0)] = input_buffer[(offset + 0)];output_buffer[(offset + 1)] = input_buffer[(offset + 1)];output_buffer[(offset + 2)] = input_buffer[(offset + 2)];output_buffer[(offset + 3)] = input_buffer[(offset + 3)];
例 : unroll-let
(defkernelmacro unroll-let ((n offset vars) &body body) (let ((unrolled-vars (gensym)) (collected-vars (gensym))) (setf unrolled-vars (loop for (name init) in vars collect (loop for i below n collect (list (gensym (symbol-name name)) init))) collected-vars (loop for i below n collect (mapcar #'list (mapcar #'car vars) (mapcar #'(lambda (x) (car (nth i x))) unrolled-vars)))) (append (list 'let (reduce #'append unrolled-vars)) (loop for expr in body append (loop for i below n collect `(symbol-macrolet ((,offset ,i) ,@(nth i collected-vars)) ,expr))))))
中間変数定義もアンロール
例 : unroll-let
(defkernel test-unroll-let (void ((input0-buffer float*) (input1-buffer float*) (output-buffer float*))) (let ((offset (* 4 (to-int (get-global-id 0))))) (unroll-let (4 i ((alfa 0.0) (bravo 0.0))) (set alfa (aref input0-buffer (+ offset i))) (set bravo (aref input1-buffer (+ offset i))) (set (aref output-buffer (+ offset i)) (+ alfa bravo)))))
例 : unroll-let
int offset = (4 * (int)(get_global_id(0)));{ float alfa898 = 0.0f; float alfa899 = 0.0f; float alfa900 = 0.0f; float alfa901 = 0.0f; float bravo902 = 0.0f; float bravo903 = 0.0f; float bravo904 = 0.0f; float bravo905 = 0.0f; alfa898 = input0_buffer[(offset + 0)]; alfa899 = input0_buffer[(offset + 1)]; alfa900 = input0_buffer[(offset + 2)]; alfa901 = input0_buffer[(offset + 3)]; bravo902 = input1_buffer[(offset + 0)]; bravo903 = input1_buffer[(offset + 1)]; bravo904 = input1_buffer[(offset + 2)]; bravo905 = input1_buffer[(offset + 3)]; output_buffer[(offset + 0)] = (alfa898 + bravo902); output_buffer[(offset + 1)] = (alfa899 + bravo903); output_buffer[(offset + 2)] = (alfa900 + bravo904); output_buffer[(offset + 3)] = (alfa901 + bravo905);}
例 : unroll-sphere
(defkernelmacro unroll-sphere ((r vx vy vz) &body body) `(progn ,@(loop for z from (- r) to r append (loop for y from (- r) to r append (loop for x from (- r) to r when (>= r (sqrt (+ (* z z) (* y y) (* x x)))) collect `(symbol-macrolet ((,vz ,z) (,vy ,y) (,vx ,x)) ,@body))))))
半径rの球へのアクセスを展開
例 : unroll-sphere
(defkernel test-unroll-sphere (void ((input-buffer float*) (output-buffer float*))) (let ((gx (to-int (get-global-id 0))) (gy (to-int (get-global-id 1))) (gz (to-int (get-global-id 2))) (sx (to-int (get-global-size 0))) (sy (to-int (get-global-size 1))) (total 0.0)) (unroll-sphere (2 vx vy vz) (set total (+ total (aref input-buffer (+ (* (+ gz vz) sy sx) (* (+ gy vy) sx) (+ gx vx)))))) (set (aref output-buffer (+ (* gz sy sx) (* gy sx) gx)) total)))
例 : unroll-let
int gx = (int)(get_global_id(0));int gy = (int)(get_global_id(1));int gz = (int)(get_global_id(2));int sx = (int)(get_global_size(0));int sy = (int)(get_global_size(1));float total = 0.0f;total = (total + input_buffer[(((((gz + -2) * sy) * sx) + ((gy + 0) * sx)) + (gx + 0))]);total = (total + input_buffer[(((((gz + -1) * sy) * sx) + ((gy + -1) * sx)) + (gx + -1))]);total = (total + input_buffer[(((((gz + -1) * sy) * sx) + ((gy + -1) * sx)) + (gx + 0))]);
… (中略) ...
total = (total + input_buffer[(((((gz + 1) * sy) * sx) + ((gy + 1) * sx)) + (gx + 0))]);total = (total + input_buffer[(((((gz + 1) * sy) * sx) + ((gy + 1) * sx)) + (gx + 1))]);total = (total + input_buffer[(((((gz + 2) * sy) * sx) + ((gy + 0) * sx)) + (gx + 0))]);output_buffer[((((gz * sy) * sx) + (gy * sx)) + gx)] = total;
おわりに
● Common LispでCUDAもOpenCLも出来る
● カーネルのチューニングにCommon Lispのマクロ使うと便利そう