SlideShare a Scribd company logo
Cell Challenge 2009参戦記
     @Kansai.pm#11


        吉田 悠一
自己紹介

• 修士2年@京都大学大学院情報学研究科
   – 専門は理論計算機科学: PNPを証明したい的分野
• エンジニア@Preferred Infrastructure (PFI)

• OSS: Anthy(かな漢字変換エンジン)など
• プログラミングコンテスト: ICPC世界大会
  (2007,2008)
• はてなブックマークの関連エントリ機能の実装。

• Web: http://lab2.kuis.kyoto-u.ac.jp/~yyoshida/
• Blog: http://mono.kmc.gr.jp/~oxy/d/
Cell Challengeとは

• マルチコア環境を効率的に利用するための並列化プロ
  グラミングでは,スケーラビリティや並行タスクの発
  見,生成,スレッド化をいかに行うかが課題。
 – (http://www.hpcc.jp/sacsis/2009/cell/ から引用)


• PS3に搭載されているCellプロセッサを使って、与えら
  れた問題をとにかく速く解くプログラムを作る。
 – 先進的計算基盤システムシンポジウム(SACSIS)の併設企画
経緯

• 2008年秋頃(?):
  – 東工大の岡本先生(実行委員)に参加しないかと誘われる。
  – Cellとかめんどそう
• 2008年12月:
  –   問題発表: 編集距離!
  –   桜庭さん(東大,PFI)がこれ速く解けるんじゃねと興味津津
  –   桜庭さんに実装を全て任せることにして口出し担当で参加。
  –   チーム名どうするかね。
ネーミング

• ハンドルネームは合体させづらい(chunとoxy)。
 – choxy, oxyn…ぴんとこない。
• Skypeで相談するもなかなか決まらない。
• そこで目についたのが弊社社長のSkype上での名前
(≧ω≦)に決
   定
経緯

• 2009年1月~:
  –   Cell the Hack
  –   メルセンヌツイスター法(乱数生成の手法)をCellで高速化
  –   Blogや2chに大量に情報が流れる
  –   こそこそと情報収集
• 2009年2月:
  – Cell Challenge 2009予選終了
  – 首位!
出典: http://www.hpcc.jp/sacsis/2009/cell/kiteiyosen2009.htm
経緯

• 2009年3月21日:
  – 本選終了
  – 結果待ち
  – 勝てると良いなぁ。
• 2009年3月22日:
  – 満を持してKansai.pmで発表
何故Kansai.pmでCell Challengeか

• ポジティブな理由:
 – 高度なプログラムを作るには、いついかなる場合も、
   アルゴリズムとアーキテクチャに対する深い造詣が必要。
 – Cellプロセッサでのプログラミングはこれらを学ぶのにとても
   良い。
• ネガティブな理由:
 – Perl読めない($$@@$@$@##@$@)
 – Webフレームワーク知らない
• 是非、今日の話をみなさんの分野でも活用させてくだ
  さい。
今年の課題:編集距離
既定課題: 編集距離

• 二つの文字列どうしの近さの測り方の一つ。
• 文字列Aを文字列Bに変形するのに必要な操作の最小回
  数。
• 操作は以下の3種類
 – 文字を挿入
 – 文字を削除
 – 文字を置換
編集距離の計算の例

• A=weight, B=write
 1. weighte (挿入:e)
 2. wrighte (置換:e → r)
 3. wrihte (削除:g)
 4. write (削除:h)
• 3回以下の操作ではAをBに変形できないのでAとBの編
   集距離は4。
問題設定

• 二つの文字列A, Bが与えられる。
• AとBの間の編集距離をCellを使って出来るだけ速く求
  めなさい。
• 制約:
 – |A|,|B| 20-128, |A||B| 34
           2               2
 – |A|,|B|は128で割り切れる。
 – 文字は1~255の255種類。
• まず以下を説明
 – Cellとはどんなプロセッサ?
 – 編集距離を求める典型的な手法
Cellの構造
SPE
SPU     SPU            SPU     SPU         SPU        SPU   SPU    SPU
 SXU        SXU        SXU     SXU         SXU        SXU   SXU    SXU

 LS         LS          LS      LS          LS         LS    LS     LS

 MFC        MFC        MFC     MFC         MFC        MFC   MFC    MFC

                                     EIB



      PPE               PPU
                                                 IO          メモリ
                  L2     L1   PXU
Cellの構造

• 周波数: 3.2GHz
• PPE(PowerPC Processor Element):
   – L1: 32KB, L2: 512KB
   – VMX
   – 全体の動作の管理に使うことが多い。
• SPE(Synergistic Processor Element):
   –   LS: 256KB
   –   128bitのレジスタが128個
   –   SIMD
   –   重たい計算に使うことが多い。
編集距離の動的計画法(DP)による解法

• 編集距離はO(nm)時間で計算出来る。(n=|A|,m=|B|)
• T[i,j]=Aの前i文字とBの前j文字の間の編集距離
         0   1   2   3   4   5   6
 T

             w   e   i   g   h   t
                                      i T[i,0]=i
 0       0   1   2   3   4   5   6
                                      j T[0,j]=j              挿入
 1   w   1   0   1   2   3   4   5
                                     T[i,j]=min(
                                                              削除
                                      T[i-1,j]+1,
 2   r   2   1   1   2   3   4   5
                                                              置換
                                      T[i,j-1]+1,
 3   i   3   2   2   1   2   3   4
                                      T[i-1,j-1]+(A[i]B[j]))
 4   t   4   3   3   2   2   3   3
 5   e   5   4   3   3   3   3   4
サンプルプログラムと(≧ω≦)のプログラム

• Cell Challengeの運営側が公開しているプログラム
  – 前述のDP+自然な7並列
  – |A|=217, |B|=217のデータで43秒ぐらい
• (≧ω≦)のプログラム
  – 予選: 同データで0.2秒ぐらい
  – 本選: 同データで0.14秒ぐらい


• 約300倍の高速化!
(≧ω≦)の手法

• 基本的な考え方は前述の動的計画法で良い。
• これをCellの特徴を生かして高速化していく。
1. アルゴリズム的な改善
2. SPE一個単位での高速化
 – SIMD, 128ビットのレジスタ
 – パイプライン, スーパスカラ, 分岐予測
 – ページング
3. SPE複数個単位での高速化
 – メモリ転送, SPE間の同期
 – SPEへのタスク配分
アルゴリズム的な改善
理論屋の考え方

• SPEがいくら7個あるとはいっても、せいぜい7倍じゃ
  ないか。
• そんな7倍程度を気にする前にアルゴリズムを改善して
  100倍速ぐらいにしとくべき。

• 実際、128bitのレジスタを使うと、先ほどのDP表の
  128要素を同時に持てる!
次の数スライドが分からなくても
  それは自然なことです。
ビット並列化[H03]

• DP表Tの隣り合った要素の差は高々1。
• PMj, D0j,HPj,HNj,VPj,VNj        {0,1}n, n=|A|を以下の様に定
  義。                                             12
• PMj[i] = 1 iff A[i] = B[j]
                                                 we
• D0j[i] = 1 iff T[i,j] = T[i-1,j-1]
                                           0     12
• VPj[i] = 1 iff T[i,j] - T[i-1,j] = 1                      1
                                                            0
                                           1w01
• VNj[i] = 1 iff T[i,j] - T[i-1,j] = -1                     0
                                           2r11
• HPj[i] = 1 iff T[i,j] - T[i,j-1] = 1                      1
                                                            0
                                                     HN
                                                     VN2=
                                                     PM2=
                                                     HP
                                                     VP
                                                     D0
                                           3i22
• HNj[i] = 1 iff T[i,j] - T[i,j-1] = -1                     1
                                                            0
                                    4   t   3   3
                                                            0
                                                            1
                                    5   e   4   3
ビット並列化[H03]

• j列目のベクトルはj-1列目のベクトルから以下のように
  計算出来る。
                                              012
• D0j=(((PMj&VPj-1)+VPj-1)&VPj-1)|PMj|VNj-1
                                               we
• HPj=VNj-1|~(D0j|VPj-1)
• HNj=D0j&VPj-1                             0 012

• VPj=(HNj<<1)|~(D0j|(HPj<<1)|1))           1w101

• VNj=D0j&((HPj<<1)|1)                      2r211
  – 各演算はbitwise                      3   i   3   2   2
                                     4   t   4   3   3
• AとBの編集距離=n+ VPj[n]- VNj[n]         5   e   5   4   3
ビット並列化

• これらのベクトルを128bitのレジスタを使って計算す
  る。
• CellにはSIMDが備わっているが高々4並列程度。それ
  に比べてビット並列化は128並列!
 – SIMD: (例えば)128bitのレジスタを32bitのレジスタ*4と思って
   32bitどうしの演算を同時に4つ実行する


• DP表Tの要素間には依存関係が有り並列化しにくいは
  ず。
• それでも並列化できているのは、依存関係を足し算の
  繰り上がりという形で表現しているから。
 – D0j=(((PMj&VPj-1)+VPj-1)&VPj-1)|PMj|VNj-1
雑感

• 今回の問題はビット並列化しなければ他をどう頑張っ
  ても勝てないものと思われる。

• 217*217のデータで1秒の壁というものが2chで議論され
  ていたが、ビット並列+7SPEによる並列を適当に実装
  すればそれだけで1秒ぐらいになる。

• しかしまだ0.14秒との間には7倍程度の差。
SPE一個単位での高速化
基礎知識: パイプライン

• 1命令を複数の工程に分けて実行する。
 – スループットを向上。クロック数の向上。レイテンシは悪化す
   る。            Cycle
              1        2        3        4        5    6    7    8
 命令1     IF       ID       EX       WB
 命令2              IF       ID       EX       WB
 命令3                       IF       ID       EX       WB
 命令4                                IF       ID       EX   WB
 命令5                                         IF       ID   EX   WB

 IF: 命令フェッチ, ID: 命令デコード, EX: 実行, WB: (レジスタ
 への)書き戻し
基礎知識: データ依存とデータハザード

• 命令が利用するデータ間に依存関係があると、前の命
  令が終わらないと後ろの命令が実行できない。
                                         Cycle
                  1    2      3      4           5    6    7    8
 add c a b   IF       ID     EX     WB
 mul d c c             ---    ---   ---     IF       ID   EX   WB

• cの値が確定するまでmul d c cが実行出来ない。
• 先ほど8サイクル5命令だったのが2命令に。
ハザードに対する対応策:ループアンローリン
グ

for (i=0;i<4;++i) { c[i] = a[i]+b[i]; d[i] = c[i]*c[i]; } ハザード!

c[0] = a[0]+b[0]; d[0] = c[0]*c[0];
c[1] = a[1]+b[1]; d[1] = c[1]*c[1];
                                      ループアンローリング
c[2] = a[2]+b[2]; d[2] = c[2]*c[2];
c[3] = a[3]+b[3]; d[3] = c[3]*c[3];

c[0] = a[0]+b[0]; c[1] = a[1]+b[1];
c[2] = a[2]+b[2]; c[3] = a[3]+b[3]; 命令を並び替えることで、
d[0] = c[0]*c[0]; d[1] = c[1]*c[1]; ハザードが緩和される
d[2] = c[2]*c[2]; d[3] = c[3]*c[3];
ループアンローリングの効用

• ループアンローリングは抜群に効く。
• データハザードが消えることで簡単に2、3倍の速度に
  なる。

• 折角なのでデモ
ハザードに対する対応策: 投機実行

•   例: 128bit整数の加算
•   Cellには128bit整数の加算命令は無い。
•   32bitの加算*4(SIMD)はあるので、これで模倣する。
•   その際繰り上がりの処理が問題になる。
    – 下の桁の繰り上がりが上の桁に影響する。
    – データ依存によりハザード発生。
• 途中で繰り上がりが発生するのとしないのを両方実行
  して、後で正しい方を選ぶようにすると速くなる(時も
  ある)。
    – 依存関係を遅延評価する。
足し算のさらなる効率化

• 普通の実装だと128bitレジスタに128行1列分の情報を
  いれることになる。
• Cellには128bitの足し算が無いので、
                            1
  32bitの足し算で模倣するしかなかった
                              32
• 32bitの足し算しかないなら、
  それに合わせてレジスタの使い方も
  変えればいいじゃない。
• つまりこういうこと。
• この管理法なら32bitの足し算*4が有れば良い。
• 結局これは実装していないが、          1
  多分速くなる。                   32

• 他のチームがこれやってないのを
  願うばかり。
基礎知識: スーパースカラ

• Cellはパイプラインを二つ持っており、同時に二つの命
  令を発行出来る。
 – Evenパイプライン: 四則演算, ビット演算など
 – Oddパイプライン: ロードストア, シフト, 分岐など
                                         Cycle
                   1        2        3           4    5    6
              IF       ID       EX          WB
    Even命令1
              IF       ID       EX          WB
    Odd命令1
                       IF       ID          EX       WB
    Even命令2
                       IF       ID          EX       WB
    Odd命令2
                                IF          ID       EX   WB
    Even命令3
                                IF          ID       EX   WB
    Odd命令3
スーパスカラの活用

• Evenパイプライン用の命令ばかり使うと、Oddパイプ
  ラインが暇になる。
 – Evenパイプラインの処理を等価なOddパイプラインを使う処
   理に変換することで高速化が望める。


• ビット並列化がビット演算ばかりするのに対し、Odd
  パイプライン用の命令はバイト単位の演算ばかりなの
  で、今回はあまり最適化できなかった。
SPE複数個単位での高速化
• SPEの数は7個なので、どんなに頑張っても7倍速にし
  かならない。
• けれども適当に実装すると7倍速にさえならないので、
  7倍に近づける努力をする必要がある。
並列化の方法

• T[i,j]を128c*128のブロックに分割する。
• 各ブロック内の値はその上と左の境界の情報があれば
  計算することが出来る。
• 各SPEに128c行を担当させ、ブロックの計算が終わる
  たびに、下の行を担当しているSPEにその情報を渡す。
• PPEはマネージャとして使用。 128
         SPE1      128c
         SPE2
         SPE3
         SPE4
         SPE1
SPEへのタスク割り当て

• 各SPEに128c行割り当てることにしたのは良いが、cを
  いくらにする?
• cが少ないと
 – 初期化やらSPE間のデータのやりとりやらが増える。
 – ループアンローリングの効果が減る。
• cが大きいと
 – レジスタが足りなくなる。
 – コンパイル時間が長くなる。
• 実験の結果cは16ぐらいが宜しい。
•   常にc=16で良いか?: NO
•   例えば行数が(128*16)*8とする。
•   SPE7個で(128*16)*7行をまず処理。
•   残った128*16行をSPE1個で処理。残りの6個ぽかーん。

• 最後まで7個のSPEが同時に動いていてほしい!
有名事実

• x,n: 任意の正整数
  sum=0
  for (i=0;i<n;++i)
       sum += (x+i)/n
  // sum == x
• 例: x=29, n=5 ⇒ (x+i)/n=5,6,6,6,6

• これを使って次のSPEに何行割り当てるかを決める。
• |A|=128x行とする。
• nに対する制約は二つ
  – nは7の倍数 ⇒ n=(適当な数)*7
  – x/nの切り上げは高々16 ⇒ n=(x+16-1)/16
• n=(x+16*7-1)/(16*7)*7とすればよい

 for (i=0;i<n;++i)
      (x+i)/n *128行を次のSPEに割り当て
基礎知識: ページング

• プログラムから見えるメモリのアドレス(仮想アドレス)
  は、ハードウェア的なメモリ上の位置を表しているわ
  けではない。

• ページング: 仮想アドレスとメモリの実際の位置の対応
  づけ。
• まだページングされていない仮想アドレスにアクセス
  すると、ページフォールトが発生し、ページングが行
  われる。
ページフォールトに対する対応策

• SPE側でページングされていないメモリにアクセスす
  ると、何故かものすごく時間がかかる模様。
 – 100万~1000万クロック程?


• PPE側で事前にメモリを初期化することでページ
  フォールトを予め起こしておくとよい。
• 具体的には一回適当にアクセスするだけ。

• デモ。
まとめ

• アルゴリズム的な改善は一番大事
 – ビット並列化
• そもそも必要な計算の量が大きく変わる。
• 有名問題なら本やら論文やらを当たればいくらでも知
  識は得られる。
• その他の最適化は基本的には同じ計算をどうやってコ
  ンピュータに速く解かせるかという問題。
• コンピュータそのものに対する知識無しに高速化出来
  るわけが無い。
 – パイプライン、ページング、キャッシュ、SIMD…
まとめ

• その後の最適化は問題依存のパズル。
 – レジスタの使い方(32bit*4)とか
 – SPEへのタスク割り当てとか
• この辺の頭の使い方はコードゴルフとかと同じ種類の
  ものな気がする。

• 流行のものを学ぶのも良いけど、そうでないところに
  も面白い話題は一杯転がっていますよ。
参考文献

• Cell Speed Challenge 2009
  http://www.hpcc.jp/sacsis/2009/cell/
• Multicore Programming Primer: PS3 Cell Programming
  http://www.cag.csail.mit.edu/ps3/index.shtml
• Heikki Hyyrö. A Bit-Vector Algorithm for Computing
  Levenshtein and Damerau Edit Distances. Proceedings
  of the Prague Stringology Conference, 29 – 32, 2003.
入力について
予選問題

•   予選の数日後に予選問題は公開された(本選はまだ)。
•   色々なサイズの問題が5個
•   Q1:
•   Q2:
•   Q3:
•   Q4:
•   Q5:
•   予選の結果を眺めると傾向が見える。
• 明らかに真面目に計算しなくても良い問題が含まれて
  いる。
• Q1: 二つの文字列が同一
• Q2: 片方の文字列が片方の文字列を含んでいる
 – abcdとbdみたいな。
• Q3: ランダム?(気づいていないだけか)
• Q4: 片方が片方の接頭辞:
 – abcdとabみたいな。
• Q5: 共通接頭辞を取り除くと使う文字がかぶっていな
  い。
 – aaabcdとaaaxyzみたいな。

More Related Content

Cell Challenge 2009 参加記