Your SlideShare is downloading. ×
0
Common Lisp最適化応用
TOYOZUMI Kouichi
@TOYOZUMIKouichi
FILMASSEMBLER
Copyright 2014 TOYOZUMI Kouichi and FILMASSEMBLER all rig...
FILMASSEMBLER 2
今日話すこと
1. MAP-IMAGEとその実装のおさらい
2. DEFUNSAFE2が「コケる」とき
3. SIMD演算による高速化
4. コンパイラマクロと関数定義情報を用いた最適化
・コード例はあくまで例で...
FILMASSEMBLER 3
MAP-IMAGE
・画像を「画素のリスト」に見立ててMAPを行います
 ・引数は画像と関数
 ・関数は「画素をとって画素を返す」関数
・例えばこんなことができます
 ・色の反転
 ・簡単な可視化
 ・合成
MA...
FILMASSEMBLER 4
その実装
・実際に「画素を関数に渡して画素を受け取る」と効率が悪い
 ・使い捨ての画素を何度も確保しなければならない
・画素を構成する成分値を多値として直接やりとりする
 ・レジスタに直接値が載るので効率が良くな...
FILMASSEMBLER 5
DEFUNSAFE2の限界
・PIXEL型をCOMPONENT型に変える
 ・単純にPIXEL型の変数を4つのCOMPONENT型の変数に
  ・LET式やLET1式で束縛している局所変数も同様
 ・(PIXEL...
FILMASSEMBLER 6
DEFUNSAFE2がコケる要因
・型指定が中途半端で追い切れなくなっちゃった
・想定外のマクロで値を束縛するようなコード
 ・俺の預かり知らないマクロで値を束縛しないでくれ
 ・MACROEXPANDで全部バラ...
FILMASSEMBLER 7
SIMD演算について
・Single Instruction Multiple Data演算、ということで
 ・単命令複情報演算、すなわち、一つの命令で複数のデータを演算
 ・同じタイプのデータを大量処理する画像...
FILMASSEMBLER 8
Common LispにおけるSIMD演算
・現時点で明示的にこれを利用できるのはSBCLのみ(と認識している)
 ・IntelのStreaming SIMD Extensions(SSE)が使えます
 ・cl-...
FILMASSEMBLER 9
やらなきゃいけないことデータ編
・転送命令はアライメントが っているとより高速なものを使えます
 ・というわけでメモリ確保時に えます
 ・Mayakaは画像IO担当のtsugumiさんに任せてます
・128bi...
FILMASSEMBLER 10
やらなきゃいけないこと演算編
・IFとかCONDなんてものはない
 ・比較結果がマスクになるのでそれをつかってコネコネします
・例:
 ・1 .入ってきたデータ
 ・2. THENの結果
 ・3. ELSEの結...
FILMASSEMBLER 11
伝家の宝刀マクロで隠
・CPIFとかCPCONDとか接頭辞CPをつけたマクロを作る
 ・これにてIFの記述が冗長になる問題は回避
 ・CPはパックされた成分値COMPONENT-PACKの頭字語
・あとはDEF...
FILMASSEMBLER 12
まだ残る問題
・DEFUNSAFE2を使わないと最適化がなされない
 ・末端ではCommon Lisp標準のDEFUNとLAMBDAで
  コトを済ましたい
・MAP-IMAGEに渡された関数はインライン展開さ...
FILMASSEMBLER 13
「あとからDEFUNSAFE2」
・DEFUNSAFE2がやっている最適化の実現
 ・少なくとも画素版の関数のソースコードが必要
 ・これさえあれば最適化がかけられる
・簡単な例:LAMBDAでMAP-IMAG...
FILMASSEMBLER 14
関数定義情報の取得
・SBCLはコンパイルされた関数がコンパイルされた時の式を
 持っている「ことがある」
 ・持っていないこともあるが、条件は不明
 ・いろいろ取得方法があるが、条件は不明
 ・他の処理系は調...
FILMASSEMBLER 15
関数定義情報の取得
コンパイラマクロと関数定義情報を用いた最適化
MAYAKA> (function-properties #'rgb->hsb)
RGB->HSB
(RED GREEN BLUE)
(COMP...
FILMASSEMBLER 16
それでも残る問題
・以下の問題はコンパイラマクロと関数定義情報を用いて解決
 ・MAP-IMAGEに渡された関数はインライン展開されない
 ・どのMAP-IMAGEを使うかを早めに自動的に決めたい
コンパイラマ...
FILMASSEMBLER 17
それでも残る問題
・MAP-IMAGEに渡された関数はインライン展開されない
 ・FLETに関数の実装を置き直し、インライン展開させる
コンパイラマクロと関数定義情報を用いた最適化
(define-compil...
FILMASSEMBLER 18
それでも残る問題
・どのMAP-IMAGEを使うかを早めに自動的に決めたい
 ・「あとからDEFUNSAFE2」をやり、最も速い使える関数を返す
コンパイラマクロと関数定義情報を用いた最適化
(defun ma...
FILMASSEMBLER 19
とりあえずおしまい
1. MAP-IMAGEとその実装のおさらい
 ・画像を画素のリストとしてあつかってMAPします
2. DEFUNSAFE2が「コケる」とき
 ・いくつか想定外の事態がありますよ
3. SI...
Upcoming SlideShare
Loading in...5
×

Common lisp最適化応用

567

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
567
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
6
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Transcript of "Common lisp最適化応用"

  1. 1. Common Lisp最適化応用 TOYOZUMI Kouichi @TOYOZUMIKouichi FILMASSEMBLER Copyright 2014 TOYOZUMI Kouichi and FILMASSEMBLER all rights reserved.
  2. 2. FILMASSEMBLER 2 今日話すこと 1. MAP-IMAGEとその実装のおさらい 2. DEFUNSAFE2が「コケる」とき 3. SIMD演算による高速化 4. コンパイラマクロと関数定義情報を用いた最適化 ・コード例はあくまで例です  ・実際に動かしてません  ・以前実装して試したことはあります ・詳細はMayakaのソースコードを見てください ・ベンチマークは大変なので、いつかまとめてな感じで  ・(ヘタなこと言うと後々難しいので) はじめに
  3. 3. FILMASSEMBLER 3 MAP-IMAGE ・画像を「画素のリスト」に見立ててMAPを行います  ・引数は画像と関数  ・関数は「画素をとって画素を返す」関数 ・例えばこんなことができます  ・色の反転  ・簡単な可視化  ・合成 MAP-IMAGEとその実装のおさらい
  4. 4. FILMASSEMBLER 4 その実装 ・実際に「画素を関数に渡して画素を受け取る」と効率が悪い  ・使い捨ての画素を何度も確保しなければならない ・画素を構成する成分値を多値として直接やりとりする  ・レジスタに直接値が載るので効率が良くなる ・コードが冗長になるのでDEFUNSAFE2というマクロで隠 する MAP-IMAGEとその実装のおさらい (defunsafe2 invert-pixel pixel ((pixel pixel)) (pixel (invert-component (pixel-red pixel)) (invert-component (pixel-green pixel)) (invert-component (pixel-blue pixel)) (pixel-alpha pixel))) (defunsafe map-image image ((function func) (image image)) (typed-let1 image result (make-image :size (image-size image)) (loop for offset fixnum from 0 below (image-length image) do (typed-multiple-value-bind ((component-t red green blue alpha)) (funcall func (image-components image offset)) (set-image-components image offset red green blue alpha))) result))
  5. 5. FILMASSEMBLER 5 DEFUNSAFE2の限界 ・PIXEL型をCOMPONENT型に変える  ・単純にPIXEL型の変数を4つのCOMPONENT型の変数に   ・LET式やLET1式で束縛している局所変数も同様  ・(PIXEL で返している画素は(COMPONENTS で置換   ・他の関数から画素を返させる場合   ・結果はCOMPONENT型の多値なのでこれを束縛   ・この関数のCOMPONENT型を返すバージョンはあるか?    ・問い合わせて存在すればどんどん置換   ・DEFUNSAFE2で定義された関数はこの検索の対象になる    ・あとは積み上げていけばおっけー DEFUNSAFE2が「コケる」とき
  6. 6. FILMASSEMBLER 6 DEFUNSAFE2がコケる要因 ・型指定が中途半端で追い切れなくなっちゃった ・想定外のマクロで値を束縛するようなコード  ・俺の預かり知らないマクロで値を束縛しないでくれ  ・MACROEXPANDで全部バラしちゃう作戦はまだやってない   ・デバッグがすごく大変そう ・例:関数AのCOMPONENT型を返すバージョン関数C-A  ・FUNCTION->COMPONENT-FUNCTIONで照会  ・存在すればまっとうなDEFUN式が返る  ・存在しないとおかしなDEFUN式が返る ・そもそも自分が論理的に満足なコード書ける自信はない  ・というわけで「コケたら」というコードを書いています DEFUNSAFE2が「コケる」とき
  7. 7. FILMASSEMBLER 7 SIMD演算について ・Single Instruction Multiple Data演算、ということで  ・単命令複情報演算、すなわち、一つの命令で複数のデータを演算  ・同じタイプのデータを大量処理する画像処理向き  ・実は転送にも効果を発揮する・・・・・・はず ・あんまり複雑なことはできない、だってアセンブラの命令ですから ・四則演算と論理演算とあとなんか文字列処理とかできるかも  ・実数の冪乗の計算を四則演算の組み合わせで実装・・・・・・(ヤメテ)  ・とは言っても画像処理では充分です SIMD演算による高速化
  8. 8. FILMASSEMBLER 8 Common LispにおけるSIMD演算 ・現時点で明示的にこれを利用できるのはSBCLのみ(と認識している)  ・IntelのStreaming SIMD Extensions(SSE)が使えます  ・cl-simdというライブラリを使います   ・SSEのイントリンシック命令が使えます   ・https://github.com/angavrilov/cl-simd  ・SBCL側ではSB-SIMD-PACKを有効化するための修正が必要  ・レジスタ長は128bitで動作確認   ・AVXの256bitレジスタは使えないみたい? ・MayakaではSSE3と4の命令をいくつか使ってます  ・自分でてきとーに命令を追加しました   ・転送や最大値最小値の取得などにもつかってます   ・ご多分に漏れず「多分」動いているものと思われます SIMD演算による高速化
  9. 9. FILMASSEMBLER 9 やらなきゃいけないことデータ編 ・転送命令はアライメントが っているとより高速なものを使えます  ・というわけでメモリ確保時に えます  ・Mayakaは画像IO担当のtsugumiさんに任せてます ・128bitのレジスタがリストになってて二つのリストにMAPする  ・16bitのデータ8個あるいは32bitのデータ4個  ・従ってメモリ上のデータの並び方に注意しないといけません   ・RGBARGBARGBARGBA....という並びにすると結果はカオス   ・RRRRRRRRGGGGGGGG....とするとみんな幸せ  ・全体の長さを128bitで分割できるようにしないと末尾で問題発生 SIMD演算による高速化 posix_memalign(&pointer, 32, (((size_t)lcm(width * height * 4, 8)) * (size_t)sizeof(int16_t)));
  10. 10. FILMASSEMBLER 10 やらなきゃいけないこと演算編 ・IFとかCONDなんてものはない  ・比較結果がマスクになるのでそれをつかってコネコネします ・例:  ・1 .入ってきたデータ  ・2. THENの結果  ・3. ELSEの結果  ・4. PREDから作られたマスク  ・5. 4.の反転  ・6. 2.と4.をAND  ・7. 3.と5.をAND  ・8. 6.と7.をOR ・やってられない SIMD演算による高速化 (if (< 5 n) (+ n 5) n) 1, 5, 9, 2, 8, 6, 3, 0 0, 0, F, 0, F, F, 0, 0 F, F, 0, F, 0, 0, F, F 6, A, E, 7, D, B, 8, 5 1, 5, 9, 2, 8, 6, 3, 0 0, 0, E, 0, D, B, 0, 0 1, 5, 0, 2, 0, 0, 3, 0 1, 5, E, 2, D, B, 3, 0 (or (and (pred d) (then d)) (and (not (pred d)) (else d)))
  11. 11. FILMASSEMBLER 11 伝家の宝刀マクロで隠 ・CPIFとかCPCONDとか接頭辞CPをつけたマクロを作る  ・これにてIFの記述が冗長になる問題は回避  ・CPはパックされた成分値COMPONENT-PACKの頭字語 ・あとはDEFUNSAFE2が成分値版に加えて成分値組版も作る  ・アルゴリズムは画素版から成分値版を作るときとさほど変わらず  ・MAP-IMAGEも専用版を作る ・どうやっても変換不可能なもの(例えばガンマ補正)  が出てくるのでそういうのが出てきたら「あきらめる」処理  ・動くようにできないことはない   ・しかし変換が難しい   ・パックされた値からそれぞれを取り出して再格納という手間    ・今のところ成分値方式でいいや SIMD演算による高速化
  12. 12. FILMASSEMBLER 12 まだ残る問題 ・DEFUNSAFE2を使わないと最適化がなされない  ・末端ではCommon Lisp標準のDEFUNとLAMBDAで   コトを済ましたい ・MAP-IMAGEに渡された関数はインライン展開されない  ・名前のついた関数オブジェクトは基本コンパイルされてる  ・LAMBDAで渡された無名関数だってコンパイル可能 ・どのMAP-IMAGEを使うかを早めに自動的に決めたい  ・明示的に指定しないと、どれを使うか決定する処理が必要   ・この処理、大抵はコンパイル時に可能   ・実行時に処理すればそれだけ遅くなる コンパイラマクロと関数定義情報を用いた最適化
  13. 13. FILMASSEMBLER 13 「あとからDEFUNSAFE2」 ・DEFUNSAFE2がやっている最適化の実現  ・少なくとも画素版の関数のソースコードが必要  ・これさえあれば最適化がかけられる ・簡単な例:LAMBDAでMAP-IMAGEに渡されている  ・これはコンパイラマクロでLAMBDA式がコンパイルされる前に   式として取得してしまえばよい   ・この式は確実に「画素をとって画素を返す」式   ・あとはこれに最適化をかけてコンパイル ・問題の例:DEFUNでコンパイルされた関数  ・MAP-IMAGEに渡されているのは単なるシンボル  ・手に入るのはコンパイル済関数   コンパイラマクロと関数定義情報を用いた最適化
  14. 14. FILMASSEMBLER 14 関数定義情報の取得 ・SBCLはコンパイルされた関数がコンパイルされた時の式を  持っている「ことがある」  ・持っていないこともあるが、条件は不明  ・いろいろ取得方法があるが、条件は不明  ・他の処理系は調査中 ・FUNCTION-LAMBDA-EXPRESSIONで取得できるのはごく一部  ・LispWorks Personal Editionでも取得できた  ・しかし取得できると話ははやい ・べつのやり方(全部引数一つです)  ・SB-DI::FUN-CODE-HEADERしたものを  ・SB-KERNEL:%CODE-DEBUG-INFOして  ・SB-C::COMPILED-DEBUG-INFO-SOURCEしたうえで  ・SB-C::DEBUG-SOURCE-FORMすると、出てくる コンパイラマクロと関数定義情報を用いた最適化
  15. 15. FILMASSEMBLER 15 関数定義情報の取得 コンパイラマクロと関数定義情報を用いた最適化 MAYAKA> (function-properties #'rgb->hsb) RGB->HSB (RED GREEN BLUE) (COMPONENT-T COMPONENT-T COMPONENT-T) COMPONENT-T (LAMBDA (RED GREEN BLUE) (DECLARE (OPTIMIZE (SPEED 3) (SPACE 0) (DEBUG 0) (SAFETY 0)) (COMPONENT-T RED GREEN BLUE) (IGNORE)) (TYPED-MULTIPLE-VALUE-BIND ((COMPONENT-T MAX-COMPONENT MIN-COMPONENT)) (MAX-AND-MIN-COMPONENT RED GREEN BLUE) (VALUES (RGB-AND-MAX-MIN->HUE RED GREEN BLUE MAX-COMPONENT MIN-COMPONENT) (MAX-AND-MIN-COMPONENT->SATURATION MAX-COMPONENT MIN-COMPONENT) MAX-COMPONENT))) 関数 L4S:function-properties (defun function-properties (func)) 関数FUNCの名前と引数リスト、引数の型リスト、返り値型および定義を多値として返します。各値が 取り出せない場合はNILを返します。 なお返された定義はLAMBDA式でありかつDECLARE式内の OPTIMIZE式は含まれないことがあります。
  16. 16. FILMASSEMBLER 16 それでも残る問題 ・以下の問題はコンパイラマクロと関数定義情報を用いて解決  ・MAP-IMAGEに渡された関数はインライン展開されない  ・どのMAP-IMAGEを使うかを早めに自動的に決めたい コンパイラマクロを用いれば、すべてがコンパイル時に行われるので 場合によっては処理の実時間を短縮できる コンパイラマクロと関数定義情報を用いた最適化
  17. 17. FILMASSEMBLER 17 それでも残る問題 ・MAP-IMAGEに渡された関数はインライン展開されない  ・FLETに関数の実装を置き直し、インライン展開させる コンパイラマクロと関数定義情報を用いた最適化 (define-compiler-macro c-map-image1 (&whole form image func) (map-image-function-lazy-eval func form (evaled-func) (if-take-func-declaration evaled-func form (declaration) ;; 定義を抽出できた場合 (with-gensyms (c-map-image1-func destination src-pixels dst-pixels) (let1 symbol (gensym) (multiple-value-bind (lmd other) (lambda-and-other declaration symbol) `(bind-destination-and-each-pixels ,image (,destination ,src-pixels ,dst-pixels) (funcall (typed-lambda ((pixels src-pixels dst-pixels) (length-t start end)) ,(replace-symbol other symbol `(flet ((,c-map-image1-func ,@(cdr lmd))) (loop for offset fixnum from start to end do (pixels-function-applied-components (src-pixels) offset ,c-map-image1-func nil (1st 2nd 3rd 4th) (set-pixels-components dst-pixels offset 1st 2nd 3rd 4th)))))) ,src-pixels ,dst-pixels 0 (image-length ,image)) ,destination)) )))))
  18. 18. FILMASSEMBLER 18 それでも残る問題 ・どのMAP-IMAGEを使うかを早めに自動的に決めたい  ・「あとからDEFUNSAFE2」をやり、最も速い使える関数を返す コンパイラマクロと関数定義情報を用いた最適化 (defun map-image-fastest-function (n func) "関数FUNCからMAP-IMAGEで使うための最も高速な関数とその関数に引数にとる型を多値として返します。" ...) (defun map-image2 (source destination func) "画像SOURCEと画像DESTINATIONを構成するすべての画素にFUNCを適用し、その結果から新たな画像を作成して返します。" (multiple-value-bind (actual-func mode) (map-image-fastest-function 2 func) (cond ((equal mode 'pixel-t) (p-map-image2 source destination actual-func)) ((equal mode 'component-t) (c-map-image2 source destination actual-func)) #+sb-simd-pack ((equal mode 'component-pack) (cp-map-image2 source destination actual-func))))) (define-compiler-macro map-image2 (&whole form source destination func) (let1 evaled-func (ignore-errors (eval func)) (if (and evaled-func (functionp evaled-func)) (multiple-value-bind (actual-func mode) (map-image-fastest-function 2 evaled-func) (cond ((equal mode 'pixel-t) `(p-map-image2 ,source ,destination ,actual-func)) ((equal mode 'component-t) `(c-map-image2 ,source ,destination ,actual-func)) #+sb-simd-pack ((equal mode 'component-pack) `(cp-map-image2 ,source ,destination ,actual-func)))) `,form)))
  19. 19. FILMASSEMBLER 19 とりあえずおしまい 1. MAP-IMAGEとその実装のおさらい  ・画像を画素のリストとしてあつかってMAPします 2. DEFUNSAFE2が「コケる」とき  ・いくつか想定外の事態がありますよ 3. SIMD演算による高速化  ・SBCLとCL-SIMDを使ってSSEのイントリンシック命令が使える  ・いろいろ手をうってやらなきゃならないことがある 4. コンパイラマクロと関数定義情報を用いた最適化  ・関数の定義時の式を実は取り出せる  ・これを使ってコンパイラマクロと組み合わせると   より強い最適化をかけられる 次回は単なる型付けやオプション付加に止まらない コード生成と変形を伴う最適化について解説します ご清聴ありがとうございました
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×