Hack the Cell 2009 反省会の反省
description
Transcript of Hack the Cell 2009 反省会の反省
Hack the Cell 2009Hack the Cell 2009
反省会の反省反省会の反省
小寺春樹
口頭で説明した概要を に書いておきます。
バイナリアン風味バイナリアン風味
Z80,68k,SH2,R3000,
R4300,ARM7,PowerPC※ なにはともあれ objdump
色々なCPUを使ってきました。Cellはまぁ、今回位では使った内には入らないですね。
ビット転置ビット転置 ((スライス)実装スライス)実装基本的な事はおいておいて、変わってるところ
レジスタ幅を一杯に使って演算(実は致命傷)
623循環で組まなかった言い訳
最初期の簡略化で思考固定化最初期の簡略化で思考固定化
mt 状態空間サイズは・ 624ワード以上の任意
・コアループ内の処理数もそれに応じて適当に変動
y = (mt[ idx & EN ]&UPPER_MASK) | (mt[ (idx+1)& EN ] & LOWER_MASK);
y = mt[ (idx+M)& EN ] ^ (y >> 1) ^ ((y&1)?MATRIX_A:0);
mt[ (idx+N)&EN ] = y;
状態空間を 2^n サイズにして、EN = 1023; // > N
インデクス計算時の mod 624 処理、 idx == N での比較分岐なりが削れる。
全部アンロールするとか考えてなかった時は、 2^nに状態空間拡張するのが単純でそこそこ速い。
レジスタ幅をいっぱいに使わないと?レジスタ幅をいっぱいに使わないと?
処理単位はレジスタサイズの 128bit
状態空間サイズが 624(623+1)だと、端数が出る
処理単位毎の処理が均一じゃなくなる
ループ当たり処理要素数は少なくとも 624/640 にな
る。
コードサイズがでかくなる(後述)ビットスライス実装にしても 2^nの方がレジスタにはまるし、なんとなく効率良いと思っちゃってそれっきり。
0 396 623
+1 +M
時間
状態空間更新のイメージ赤横線が読み出しで、計算して、青に書き込む感じ。
0 396 623
+1 +M
時間
実際は矢印部分はレジスタで引き渡しますので、メモリのロード&ストアはライン当たり32ずつで済んでます。
0 396 623
+1 +M
時間
に割り当てられるレジスタが1ライン毎に変わる -> コアループは偶数ラインで構成
even/odd even/odd のバランス調整のバランス調整
基本的に evenが重いので、ステップ数増加を抑えつつodd側に振れる仕事を振っていきます。
0 396 623
+1 +M
時間
+Mの値を取得するところ、2つのレジスタを結合して回す処理のところで調整を行います。
A B
sel dst,A,B,mask
AB
rotqbyi dst,dst,#14rotqbii dst,dst,#4
A B
A B
AB
AB B
B B
A B
shufb dst,A,B,shufc1
rotqbii dst,dst,4
rotqbii tmp,B,4
shufb dst,dst,tmp,shufc2
=
even*1,odd*2 odd*4
even-1,odd+2となる代替処理の説明。なんとなく出来る事が分かって貰えればいいかなぁ。
temperingtempering
temperingも一応考えました
n=[]
def eor(a,b)
puts "(#{a}^#{b})"
"(#{a}^#{b})"
end
0.upto(31){|i| n[i]="n#{i}" }
31.downto(11){|i| n[i]=eor(n[i],n[i-11]) }
[0,3,4,5,7,10,12,13,17,19,21,22,24].each{|i| n[i] = eor(n[i],n[i+7]) }
[0,1,2,4,5,6,7,8,9,13,14].each{|i| n[i] = eor(n[i],n[i+15]) }
31.downto(18){|i| n[i] = eor(n[i],n[i-18]) }
こんな Rubyスクリプトを書いて、各ビットに元ビットがどう反映されているかを調べます。
(n31^n20)
(n30^n19)
(n29^n18)
(n28^n17)
(n27^n16)
(n26^n15)
(n25^n14)
(n24^n13)
(n23^n12)
(n22^n11)
(n21^n10)
(n20^n9)
(n19^n8)
(n18^n7)
(n17^n6)
(n16^n5)
(n15^n4)
(n14^n3)
(n13^n2)
(n12^n1)
(n11^n0)
(n0^n7)
(n3^n10)
(n4^(n11^n0))
(n5^(n12^n1))
(n7^(n14^n3))
(n10^(n17^n6))
((n12^n1)^(n19^n8))
((n13^n2)^(n20^n9))
((n17^n6)^(n24^n13))
((n19^n8)^(n26^n15))
((n21^n10)^(n28^n17))
((n22^n11)^(n29^n18))
((n24^n13)^(n31^n20))
((n0^n7)^(n15^n4))
(n1^(n16^n5))
(n2^((n17^n6)^(n24^n13)))
((n4^(n11^n0))^((n19^n8)^(n26^n15)))
((n5^(n12^n1))^(n20^n9))
(n6^((n21^n10)^(n28^n17)))
((n7^(n14^n3))^((n22^n11)^(n29^n18)))
(n8^(n23^n12))
(n9^((n24^n13)^(n31^n20)))
(((n13^n2)^(n20^n9))^(n28^n17))
((n14^n3)^(n29^n18))
((n31^n20)^(((n13^n2)^(n20^n9))^(n28^n17)))
((n30^n19)^((n12^n1)^(n19^n8)))
((n29^n18)^(n11^n0))
((n28^n17)^(n10^(n17^n6)))
((n27^n16)^(n9^((n24^n13)^(n31^n20))))
((n26^n15)^(n8^(n23^n12)))
((n25^n14)^((n7^(n14^n3))^((n22^n11)^(n29^n18))))
(((n24^n13)^(n31^n20))^(n6^((n21^n10)^(n28^n17))))
((n23^n12)^((n5^(n12^n1))^(n20^n9)))
(((n22^n11)^(n29^n18))^((n4^(n11^n0))^((n19^n8)^(n26^n15))))
(((n21^n10)^(n28^n17))^(n3^n10))
((n20^n9)^(n2^((n17^n6)^(n24^n13))))
(((n19^n8)^(n26^n15))^(n1^(n16^n5)))
((n18^n7)^((n0^n7)^(n15^n4)))
出力はこんな感じ。一番左側がターゲットビット。
…これをよく睨むと
(n31^n20)
(n30^n19)
(n29^n18)
(n28^n17)
(n27^n16)
(n26^n15)
(n25^n14)
(n24^n13)
(n23^n12)
(n22^n11)
(n21^n10)
(n20^n9)
(n19^n8)
(n18^n7)(n18^n7)
(n17^n6)
(n16^n5)
(n15^n4)
(n14^n3)
(n13^n2)
(n12^n1)
(n11^n0)
(n0^n7)(n0^n7)
(n3^n10)
(n4^(n11^n0))
(n5^(n12^n1))
(n7^(n14^n3))
(n10^(n17^n6))
((n12^n1)^(n19^n8))
((n13^n2)^(n20^n9))
((n17^n6)^(n24^n13))
((n19^n8)^(n26^n15))
((n21^n10)^(n28^n17))
((n22^n11)^(n29^n18))
((n24^n13)^(n31^n20))
((n0^n7)^(n15^n4))((n0^n7)^(n15^n4))
(n1^(n16^n5))
(n2^((n17^n6)^(n24^n13)))
((n4^(n11^n0))^((n19^n8)^(n26^n15)))
((n5^(n12^n1))^(n20^n9))
(n6^((n21^n10)^(n28^n17)))
((n7^(n14^n3))^((n22^n11)^(n29^n18)))
(n8^(n23^n12))
(n9^((n24^n13)^(n31^n20)))
(((n13^n2)^(n20^n9))^(n28^n17))
((n14^n3)^(n29^n18))
((n31^n20)^(((n13^n2)^(n20^n9))^(n28^n17)))
((n30^n19)^((n12^n1)^(n19^n8)))
((n29^n18)^(n11^n0))
((n28^n17)^(n10^(n17^n6)))
((n27^n16)^(n9^((n24^n13)^(n31^n20))))
((n26^n15)^(n8^(n23^n12)))
((n25^n14)^((n7^(n14^n3))^((n22^n11)^(n29^n18))))
(((n24^n13)^(n31^n20))^(n6^((n21^n10)^(n28^n17))))
((n23^n12)^((n5^(n12^n1))^(n20^n9)))
(((n22^n11)^(n29^n18))^((n4^(n11^n0))^((n19^n8)^(n26^n15))))
(((n21^n10)^(n28^n17))^(n3^n10))
((n20^n9)^(n2^((n17^n6)^(n24^n13))))
(((n19^n8)^(n26^n15))^(n1^(n16^n5)))
((n18^n7)^((n0^n7)^(n15^n4)))((n18^n7)^((n0^n7)^(n15^n4)))
この辺なんか無駄っぽい。 (n18^n7)は最後にしか出て来ないのに、n7は結局打ち消されてたり。
(n31^n20)
(n30^n19)
(n29^n18)
(n28^n17)
(n27^n16)
(n26^n15)
(n25^n14)
(n24^n13)
(n23^n12)
(n22^n11)
(n21^n10)
(n20^n9)
(n19^n8)
(n18^n7)->x
(n17^n6)
(n16^n5)
(n15^n4)
(n14^n3)
(n13^n2)
(n12^n1)
(n11^n0)
(n0^n7)->x
(n3^n10)
(n4^(n11^n0))
(n5^(n12^n1))
(n7^(n14^n3))
(n10^(n17^n6))
((n12^n1)^(n19^n8))
((n13^n2)^(n20^n9))
((n17^n6)^(n24^n13))
((n19^n8)^(n26^n15))
((n21^n10)^(n28^n17))
((n22^n11)^(n29^n18))
((n24^n13)^(n31^n20))
((n0^n7)^(n15^n4)) -> (n0^(n15^n4)) (n0^(n15^n4)) , ((n0^(n15^n4))^n7)((n0^(n15^n4))^n7)
(n1^(n16^n5))
(n2^((n17^n6)^(n24^n13)))
((n4^(n11^n0))^((n19^n8)^(n26^n15)))
((n5^(n12^n1))^(n20^n9))
(n6^((n21^n10)^(n28^n17)))
((n7^(n14^n3))^((n22^n11)^(n29^n18)))
(n8^(n23^n12))
(n9^((n24^n13)^(n31^n20)))
(((n13^n2)^(n20^n9))^(n28^n17))
((n14^n3)^(n29^n18))
((n31^n20)^(((n13^n2)^(n20^n9))^(n28^n17)))
((n30^n19)^((n12^n1)^(n19^n8)))
((n29^n18)^(n11^n0))
((n28^n17)^(n10^(n17^n6)))
((n27^n16)^(n9^((n24^n13)^(n31^n20))))
((n26^n15)^(n8^(n23^n12)))
((n25^n14)^((n7^(n14^n3))^((n22^n11)^(n29^n18))))
(((n24^n13)^(n31^n20))^(n6^((n21^n10)^(n28^n17))))
((n23^n12)^((n5^(n12^n1))^(n20^n9)))
(((n22^n11)^(n29^n18))^((n4^(n11^n0))^((n19^n8)^(n26^n15))))
(((n21^n10)^(n28^n17))^(n3^n10))
((n20^n9)^(n2^((n17^n6)^(n24^n13))))
(((n19^n8)^(n26^n15))^(n1^(n16^n5)))
((n18^n7)^((n0^n7)^(n15^n4))) -> (n18^(n0^(n15^n4))) (n18^(n0^(n15^n4)))
こう変形すると、1ステップだけ減ります。これ以上減らないの?わかりません。
コード生成(並び替え)コード生成(並び替え)
e cntb $r14,$r14
e cntb $r30,$r30
e sumb $r14,$r30,$r14
r $r30
e cntb $r15,$r15
e cntb $r31,$r31
e sumb $r15,$r31,$r15
r $r31
o shufb $r0,$r0,$r8,shufc
r $r8
e a %0,%0,$r0
r $r0・発行順依存関係調べます・発行可能な中では単純に先着優先。あとはベースコード側の工夫でなんとかします。・ r っていうのはレジスタのリリース。まぁ、自動化するまでもないかなぁ、と。
レジスタの動的割り当てによる再利用レジスタの動的割り当てによる再利用
参照完了直後に再利用。999ステップ目等は dual issue で参照完了と同時にターゲットになってたり。
仕上げ仕上げ
結果加算は結果加算は 88本の累積レジスタで処理本の累積レジスタで処理
0.upto(15){ |x|i = (x&1)*8+(x/2)@asm<<"e cntb $r#{i},$r#{i}"@asm<<"e cntb $r#{i+16},$r#{i+16}"@asm<<"e sumb $r#{i},$r#{i+16},$r#{i}"
}0.upto(7){|i|
@asm<<"o shufb $r#{i},$r#{i},$r#{i+8},shufc"@asm<<"e a %#{i},%#{i},$r#{i}"
}※shufc = {1,17,3,19, 5,21,7,23, 9,25,11,27, 13,29,15,31}
ライン当たりコストライン当たりコスト ::cntb*32, sumb*16, shufb*8, a*8cntb*32, sumb*16, shufb*8, a*8
even56,odd8と、合計ステップ数 64でも oddに 8振れているのがポイント
cntb cnt
b
cntbcnt
b
sumb
sumb
shufbbit0,8,16,24
累積レジスタa
bit24bit8
bit16bit0
例)例) bit0,8,16,24bit0,8,16,24の集計の集計
+ +
+ +
※bit0 = MSB
8ビット間隔の重みのデータを cntb,sumb,shufbで組み上げる事で、SIMDワードとしてそのまま加算出来ますね、という話なのですがこれもあまり説明うまくなかったですね。
その他その他
MT状態空間の 3/32をレジスタに常駐
連続メモリアクセスストールの回避
端数の処理も SIMD化している
1回限りのビット転置も高速化している
カウンタを oddパイプで実装
実行後のmtは問われないから、端数の処理を先にやる様にするとビット転置は1回で済んで楽ですよね。ん、そうでも
ない?
rotqbiiで 7bitカウンタを作る話
おしまいおしまい