22
Common LispGPGPU 関東 GPGPU勉強会 #4 2016-08-21 gos-k

Common LispでGPGPU

  • Upload
    gos-k

  • View
    358

  • Download
    1

Embed Size (px)

Citation preview

Page 1: Common LispでGPGPU

Common LispでGPGPU

関東GPGPU勉強会 #42016-08-21

gos-k

Page 2: Common LispでGPGPU

自己紹介

● 名前 : gos-k

– gos_k : twitter, bitbucket, qiita

– gos-k : github, hatenablog

● 仕事 : 渋谷の某社でWebプログラマ

– Common LispとかReact.jsとか

– それ以前は産業用機器のソフトや3D画像処理のチューニングの仕事

● 趣味 : パンケーキ

Page 3: Common LispでGPGPU

発表の流れ

1) Common Lispに関する簡単な説明

2) Common LispにおけるGPGPU関連の現状

3) Common LispでOpenCL

4) Common Lispでカーネルコードのマクロ展開

Page 4: Common LispでGPGPU

今流行りのLisp方言

● Clojure

– JVMで動くヤツ

● Common Lisp

– Lisp方言を寄せ集めたヤツ

● Scheme

– 関数型のヤツ

● Emacs Lisp

– Emacsで動くヤツ

今回はCommon Lispを扱う

Page 5: Common LispでGPGPU

Common Lispとは

● 1980年代に数々のLispマシンやLisp方言が生まれた

● コミュニティ分裂の懸念を解消すべく、Common Lispとして標準化を行う

– 既存のLispから最良の機能を集結

– ANSIから1996年に標準規格リリース

● 制御構造、再帰関数呼び出し、ガベージコレクション、インクリメンタルコンパイル、オブジェクトシステム、コンディションシステム等々を持つ

参考文献 : Peter, 実践 Common Lisp, 2008

Page 6: Common LispでGPGPU

関連用語

● SBCL (Steel Bank Common Lisp)

– Common Lispの処理系

– 色々あるけど非商用の処理系ならとりあえずコレ

● Quicklisp

– Common Lispのパッケージ管理ツール

– pipとかgemとかnpmとかの仲間?

● Roswell

– 処理系管理ツール?

– デフォルトでSBCLとQuicklispが入って、直ぐに使い始められる

Page 7: Common LispでGPGPU

発表の流れ

1) Common Lispに関する簡単な説明

2) Common LispにおけるGPGPU関連の現状

3) Common LispでOpenCL

4) Common Lispでカーネルコードのマクロ展開

Page 8: Common LispでGPGPU

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で書ける

Page 9: Common LispでGPGPU

発表の流れ

1) Common Lispに関する簡単な説明

2) Common LispにおけるGPGPU関連の現状

3) Common LispでOpenCL

4) Common Lispでカーネルコードのマクロ展開

Page 10: Common LispでGPGPU

cl-oclapi

● 3bさんのOpenCL APIは動くけど

– Quicklisp対応してない

– 何年もメンテされてない?

● OpenCL 1.2 APIのラッパーをスクラッチから作った

– 数年だれもやってなかったネタなのに、guichoさんと数週間差でネタかぶり

– しかし、その他色々なライブラリの都合により、cl-oclapiがQuicklispのGPGPU関連で一番最初に登録される

Page 11: Common LispでGPGPU

oclcl

● cl-cudaからフォークして、カーネル部分取り出し

– S式からCを生成

– マクロのサポート

– シンボルマクロのサポート

● 変更点

– キーワードの置換や組込み関数の拡充

● __global__を__kernelにしたり__synchronizeをbarrierにしたり

– スカラ型ベクタ型のサポート拡大

– テストコードのOpenCL APIライブラリをeazy-openclとcl-oclapiに

– Quicklisp登録済み

Page 12: Common LispでGPGPU

発表の流れ

1) Common Lispに関する簡単な説明

2) Common LispにおけるGPGPU関連の現状

3) Common LispでOpenCL

4) Common Lispでカーネルコードのマクロ展開

Page 13: Common LispでGPGPU

Common Lispで?

● Cでカーネル書きたくない

– 不便だから

– カーネルの言語は規格上はCのみ

– その他言語はAPI対応ばかりで、カーネルまで書けるのはあまりない

● 自分で作れるか

– Common Lispなら既にcl-cudaがある

● これをベースにすればOpenCLも出来るのでは

– マクロ使えばカーネル最適化に便利そう

Page 14: Common LispでGPGPU

Common Lispで?

● マクロで変数が使える

● マクロで関数が使える

● マクロで条件分岐が使える

● マクロでループが使える

● マクロでプリント文が使える

● マクロを一段階展開したものが得られる

マクロで普通にプログラミング出来る

Page 15: Common LispでGPGPU

例 : 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)];

Page 16: Common LispでGPGPU

例 : 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))))))

中間変数定義もアンロール

Page 17: Common LispでGPGPU

例 : 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)))))

Page 18: Common LispでGPGPU

例 : 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);}

Page 19: Common LispでGPGPU

例 : 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の球へのアクセスを展開

Page 20: Common LispでGPGPU

例 : 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)))

Page 21: Common LispでGPGPU

例 : 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;

Page 22: Common LispでGPGPU

おわりに

● Common LispでCUDAもOpenCLも出来る

● カーネルのチューニングにCommon Lispのマクロ使うと便利そう