Top Banner
キキキキキキキキキキキキキキキキキキキキキキ キキ #DSIRLNP @kumagi
60

キャッシュコヒーレントに囚われない並列カウンタ達

Jun 16, 2015

Download

Technology

Kumazaki Hiroki

第6回DSIRNLPで発表した資料。
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: キャッシュコヒーレントに囚われない並列カウンタ達

キャッシュコヒーレントに囚われない並列カウンタ達

  #DSIRLNP@kumagi

Page 2: キャッシュコヒーレントに囚われない並列カウンタ達

この発表について

DSIRData Structure!

Page 3: キャッシュコヒーレントに囚われない並列カウンタ達

キャッシュ?• CPU が高速化のためにメモリの一部を切り

出して複製している物

Page 4: キャッシュコヒーレントに囚われない並列カウンタ達

キャッシュCPU コア

L1 キャッシュ

L2 キャッシュ

L3 キャッシュ

メモリ

Page 5: キャッシュコヒーレントに囚われない並列カウンタ達

All programmer should know

• L1 キャッシュ 参照 ......................... 0.5 ns• 分岐予測ミス ............................ 5 ns• L2 キャッシュ 参照 ........................... 7 ns• Mutex lock/unlock ........................... 25 ns• Main memory 参照 ...................... 100 ns

Page 6: キャッシュコヒーレントに囚われない並列カウンタ達

キャッシュ

メモリ

Page 7: キャッシュコヒーレントに囚われない並列カウンタ達

キャッシュコヒーレント?• 複数の CPU コアから見えるメモリは同一

でないと困る• つまり複数の CPU コアのキャッシュは常

に最新の情報を保持してないと困る• だが常に最新の情報を全部のキャッシュ

に全て書き続けるのは速度が出ないので、まるで本当に全部のキャッシュにすべて書いてるかのように見せかけながらパフォーマンスを出す必要がある

Page 8: キャッシュコヒーレントに囚われない並列カウンタ達

キャッシュ

メモリ

2 つのコアでL2 キャッシュを共有

Page 9: キャッシュコヒーレントに囚われない並列カウンタ達

キャッシュコヒーレント• 複数の L1 キャッシュ間で一貫したデータを扱

うためのキャッシュ間のプロトコル• Intel 系 CPU は MESIF プロトコル• AMD 系 CPU は MOESI プロトコル

Core1 Core2

L1 キャッシュ L1 キャッシュ

L2 キャッシュ

コヒーレント

Page 10: キャッシュコヒーレントに囚われない並列カウンタ達

キャッシュコヒーレントプロトコル

• キャッシュラインが取る状態名の頭文字が由来– Modified: メモリよりもキャッシュの方が新しい ( 書き換

えた )– Exclusive: メモリとキャッシュが同一であり、他のコアは

このキャッシュラインを持っていない– Shared: メモリとキャッシュが同一であり、他のコアも

このキャッシュラインを複製している– Invalid: 正しいキャッシュを持っていないので読むな– Owned: 俺がメモリだ (Shared 可能な Modified)– Forward: Shared のボス。アクセスする際にはこいつに伺

Page 11: キャッシュコヒーレントに囚われない並列カウンタ達

L1 キャッシュの状態• 1 ライン 64byte で、アドレスの下位バイ

トが同じ物ごとに 8way ずつ 32KB まで持っている

• 1 ラインごとに MESIF のどれかのステートを持っているinvalid

shared

exclusive

modified

64Byte

32KB

全部のラインが独立してそれぞれステートが遷移する

forward

Page 12: キャッシュコヒーレントに囚われない並列カウンタ達

共有した

キャッシュコヒーレントプロトコル

• MESI(F) プロトコルは Modified なデータを他のコアが読み出す際にメモリに書き戻す

• Shared なデータを書き換える時には Invalidation 要求をブロードキャストするためトラフィックが混む

Modified

Shared Invalid

Exclusive

共有を要求( 1 個下のメモリへアクセス)

Invalidation要求が来た

新規に読み出した

書き換えた

他のコアから貰った

Page 13: キャッシュコヒーレントに囚われない並列カウンタ達

キャッシュを汚す事はギルティ• 他のスレッドから繰り返し読む値を書き換え続けると、

キャッシュラインのステートは Modified と Shared の間を行ったりきたりすることになる。これは激しいトラフィックを起こす。

• 更には Modified から Shared になる度に下層のメモリに書き込まなくてはならない

Core1 Core2

L1 キャッシュ L1 キャッシュ

L2 キャッシュ

write

read

うぉー!

うぉー!

ぎゃー!!write

Invalidate

Page 14: キャッシュコヒーレントに囚われない並列カウンタ達

キャッシュを汚す事はギルティ

http://www.1024cores.net/home/lock-free-algorithms/first-things-first から

共有データにWrite した場合の速度

local データにWrite した場合の速度

共有 /local データから Read した場合の速度

Page 15: キャッシュコヒーレントに囚われない並列カウンタ達

http://www.1024cores.net/home/lock-free-algorithms/first-things-first から

まったくスケールしない!!!!

Page 16: キャッシュコヒーレントに囚われない並列カウンタ達

NUMA でのキャッシュコヒーレント

• 最近のサーバはマルチソケット CPU がよく使われる

Page 17: キャッシュコヒーレントに囚われない並列カウンタ達

cc-NUMA

• 複数の CPU ソケットから同一のメモリ空間が見えて欲しい(まるでマルチコア CPUのように)

• キャッシュ衝突すると QPI アクセスの刑– かつては拷問にも使われていた QPI 経由の

キャッシュコヒーレント

Page 18: キャッシュコヒーレントに囚われない並列カウンタ達

All programmer should know

• L1 キャッシュ 参照 ......................... 0.5 ns• 分岐予測ミス ............................ 5 ns• L2 キャッシュ 参照 ........................... 7 ns• Mutex lock/unlock ........................... 25 ns• Main memory 参照 ...................... 100 ns• QPI 経由で隣のメモリ参照 .............. 200 ns

Page 19: キャッシュコヒーレントに囚われない並列カウンタ達

キャッシュ

メモリ メモリ

隣の CPU のキャッシュは自分のメインメモリよりも遠い!!!

Page 20: キャッシュコヒーレントに囚われない並列カウンタ達

cc-NUMA の時代• キャッシュ衝突をとにかく減らすアルゴ

リズムが望まれている

Page 21: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree

• 以下のインタフェースを備えるカウンタ– add(int a) :数値 a を足す– read() :現在の数値を読む

• ただしスケーラブル!class counter {public: counter() : cnt_(0) {} void add(int a) { cnt_ += a; } int read() const { return cnt_; }private: int cnt_;};

擬似コード

Page 22: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree

• 1 つのキャッシュラインを取り合うのが 2スレッドまでになるようトーナメントを構成する

• トーナメントでぶつかったスレッドは、先に来たスレッドに後に来たスレッドが値を託して待つ

• キャッシュコヒーレントトラフィックを劇的に削減!

Page 23: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree

• トーナメントのてっぺんを読めば値が読める

root

Page 24: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree アルゴリズム

+1 +1 +2 +3 +2 +1

Page 25: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree アルゴリズム

+1 +1 +2 +3 +2 +1

Page 26: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree アルゴリズム

+1

+3

+3

+3

Page 27: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree アルゴリズム

+4

+6

Page 28: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree アルゴリズム

+10

Page 29: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree アルゴリズム• 結合法則を用いて計算の合成を行う• x + 1 + 1 + 2 + 3 + 2 + 1• x + 1 + (1 + 2) + 3 + (2 + 1)• x + (1 + 3) + (3 + 3)• x + (4 + 6)• x + 10

Page 30: キャッシュコヒーレントに囚われない並列カウンタ達

詳細なアルゴリズム• 各ノードは Idle, First, Second, Root のどれ

かの状態を持つ– Idle: どのスレッドも触ってない– First: 最初のスレッドが既に触った– Second: 二つ目のスレッドが触った– Root: てっぺん ( 遷移しない )

• 更にノードはロックを 2 つ持っている

Page 31: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree• 初めに木を登りながらステートの変更を

行う– Idle な物を First にしていく– ロックを取ってステートを変えて即アンロッ

クRoot

First

First

Page 32: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree• 初めに木を登りながらステートの決定を

行う– First を見たら Second にして第二ロックを獲得• こっちのロックはすぐには手放さないRoot

First

First First

Second

Second

Page 33: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree• 初めに木を登りながらステートの決定を

行う– Second にしたらそこで木を登るの停止

Root

First

First

Second

Second

Page 34: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree• 木を登るのをやめた所で、自分の過去の

経路の第二ロックを獲得し直しながら値を清算

Root

First

First

Second

Second

Page 35: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree• 木を登るのをやめた所で、自分の過去の

経路の第二ロックを獲得し直しながら値を清算

Root

First

First

Second

Second+2+1

Page 36: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree• 清算しきった値を最初に自分が登った一番高い所に書き込んでアンロックするのが基本戦略

Root

First

First

Second

Second

+3Second

Page 37: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree• 清算しきった値を最初に自分が登った一番高い所に書き込んでアンロック

Root

FirstSecond

Second

Second

FirstFirst

+3

Page 38: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree• Second のステートを見たら他のスレッド

が清算中なのでそれを待つ(第二ロック獲得待ち)

Root

FirstSecond

Second

Second

FirstFirstLock!

Lock!

Page 39: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree• 清算し終わった値を持って再帰的に自分

の値として生産する

Root

FirstSecond

Second

Second

FirstFirst

+4

Page 40: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree

• 平均して木の深さ n に対して 2*n回のロックとアンロックを使うことになる–大丈夫なの・・・?

• Mutex lock/unlock ........................... 25 ns• QPI 経由で隣のメモリ参照 .............. 200 ns

キャッシュ衝突のペナルティを考えると余裕でペイする

Page 41: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Tree

• 衝突がない場合でも 2*n回のロック・アンロック

• レイテンシという点において非常に悪い–改良として 1 ノードに 3 スレッド以上ぶら下げて木の深さを減らすパターンもあるが、複雑さがマッハでレイテンシはむしろ悪化

Page 42: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Funnel

• Funnel = 上戸• 乱数ベースで負荷を低減– アルゴリズムは Elimination に近い– Elimination と組み合わせることも可能

Counter

Page 43: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Funnel

• 配列の中のランダムな箇所に CAS命令で自分のタスクを置く

• ランダム時間の待機後、 CAS でタスクを引き上げて次のレイヤーに進む ( ここが上戸っぽい)

+2 +1

Page 44: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Funnel

• 置こうと思った場所に先客が居たらそいつと操作を結合して次のレイヤーに進む

+2 +1

+3 したい

Page 45: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Funnel

• 置こうと思った場所に先客が居たらそいつと操作を結合して次のレイヤーに進む

待 +1

+5 せざるを得ない!

Page 46: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Funnel

• 置こうと思った場所に先客が居たらそいつと操作を結合して次のレイヤーに進む

待 +1

+5

Counter

あっ待てって言われてる

Page 47: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Funnel

• カウンタのレイヤーまで到達したらそいつに Compare And Swap

• 感動的に簡単しかも速い

Page 48: キャッシュコヒーレントに囚われない並列カウンタ達

Combining Funnel

Combining Funnels A Dynamic Approach To Software Combining より

Page 49: キャッシュコヒーレントに囚われない並列カウンタ達

SNZI

• 数を数えようとするから残念なことになるんや!諦めろ!–ゼロかどうか分かればええ!

• Scalable-Non-Zero-Indicator の略で SNZI– pronounced "snazzy" : (和訳 )粋な、おしゃれ

な、優雅な、エレガントな、格好いい

Page 50: キャッシュコヒーレントに囚われない並列カウンタ達

SNZI のセマンティクス• 数字が読めないカウンタ• これをスケーラブルにする

class snzi {public: counter() : cnt_(0) {} void inc() { cnt_ += 1; } void dec() { cnt_ -= 1; } bool is_zero() const { return cnt_ == 0; }private: int cnt_;};

擬似コード

Page 51: キャッシュコヒーレントに囚われない並列カウンタ達

SNZI の実装• キャッシュラインで木構造を作る

0

0 0

0 0 0 0

ここが 0 かどうかを

見れば良い

Page 52: キャッシュコヒーレントに囚われない並列カウンタ達

SNZI の実装• 木の各ノードが 1CPU–上から数えて i番目のノードは i番目の CPU に割当

0

0 0

0 0 0 01

1

1

root 以外を 0 から 1 に増やすときには 1 個上のノードの値をインクリメントするroot は常に普通にインクリメントするだけ

Page 53: キャッシュコヒーレントに囚われない並列カウンタ達

SNZI の実装• 木の各ノードが 1CPU–上から数えて i番目のノードは i番目の CPU に割当

0

0 0

0 0 0 01

1

1

root 以外を 1 から 0 に減らすときには 1 個上のノードの値をデクリメントするroot は常に普通にデクリメントするだけ

Page 54: キャッシュコヒーレントに囚われない並列カウンタ達

SNZI の実装• 木の各ノードが 1CPU–上から数えて i番目のノードは i番目の CPU に割当

0

0 0

0 0 0 01

1

1

それ以外のときは普通にインクリメントするだけ=上のノードのキャッシュラインに触れずに済む

2

Page 55: キャッシュコヒーレントに囚われない並列カウンタ達

SNZI• すごくスケールする!!!

SNZI: Scalable NonZero Indicators より

Page 56: キャッシュコヒーレントに囚われない並列カウンタ達

しかもロックフリー!!

Page 57: キャッシュコヒーレントに囚われない並列カウンタ達

SNZI の実装• 厳密には複数のスレッドが同一のノード

にアクセスしに来た際に 0 1⇔ 周りで細かい調停が必要

0 から 1 のときは、先に自分の箇所の値を 1/2 という値にしてから上のノードをインクリメントしにいき、そのあとで 1/2 を 1 に CAS を試みる。もし上のノードのインクリメントに成功した後に 1/2→1 の CAS に失敗したら上のノードをデクリメントしておく• デクリメントの数はその前に行われたイン

クリメントの数を越えては行けない( Read-Write ロックなどに用いるには充分)

Page 58: キャッシュコヒーレントに囚われない並列カウンタ達

SkySTM への適用

What kinds of applications can benefit from Transactional Memory? より

Page 59: キャッシュコヒーレントに囚われない並列カウンタ達

時間が無くて書けなかったこと• STM の高速化のために SNZI が必要とか

いっときながら実際に SNZI を Read-Write-Lock に用いた SkySTM は割とあっさりTLRW に負けている– TLRW は ByteLock っていうまた別のロックプ

ロトコルを用いてる(こっちの話はまた今度)

• SNZI のために HTM を用いたやつがあって凄い速い– HTM の使い方についてもまた今度

Page 60: キャッシュコヒーレントに囚われない並列カウンタ達

HTM+SNZI

HTMの力

What kinds of applications can benefit from Transactional Memory? より