Upload
takashi-hoshino
View
3.487
Download
4
Embed Size (px)
DESCRIPTION
Intel TSX HLE の可能性を探るべく,いくつかのマイクロベンチマークで性能評価した.
Citation preview
Intel TSX HLE を触ってみた
x86/x64最適化勉強会6 2013-08-31
星野喬@サイボウズ・ラボ / @starpoz
1
自己紹介
• 星野 喬(@starpoz) – サイボウズ・ラボ
• 興味 – データベース,ストレージ,分散アルゴリズムなど
• 今の仕事: #walbdev – Linux における block device の効率的な 増分記録ドライバ • http://developer.cybozu.co.jp/tech/?p=5130
2
今日の話
• Intel の最新 CPU (Haswell) の 新機能のひとつ TSX HLE を使い 性能評価をしてみて トランザクショナルメモリの可能性を探る
• 後追い実験大歓迎 J
3
トランザクショナルメモリ (TM)
• 「Intel TSX について」参照 – http://www.slideshare.net/starpos/intel-tsx-
x86opti4
• 要約: atomic メモリアクセスを実現する技術 – 他と競合したくない一連のメモリアクセス – Lock-free な時代がすぐそこに(反論アリ
4
Intel TSX
• Transactional Synchronization eXtension • 2 つのインターフェースを提供 – HLE: Lock prefix の拡張で細粒度の排他を実現 – RTM: 制約はあるが HardwareTM そのもの
• 楽観的な振舞 • CPU L1 キャッシュ上で必要なデータを管理 • キャッシュライン単位での競合検出
5
競合 (collision)
• あるリソースへの複数主体からのアクセスが同時に起きること(要出典)
• Intel TSX の場合: – リソース: キャッシュライン単位のメモリ領域 – 主体: スレッド – アクセス: read/write (全て read の場合を除く) – 同時: クリティカルセクションの実行期間が重複
6
HLE: Hardware Lock Elision
• 楽観的挙動 –(失敗)à 悲観的挙動 • 失敗すると電気代が無駄になる
7
競合発生しないケース 競合発生するケース
ロックを無視して 投機実行開始
競合発生しなかった めでたしめでたし
競合発生 変更破棄 ロック待ち体制
ロック取れたので 必ず実行できる
時間 時間
Spinlock with HLE
• GCC 4.8 の場合 (マニュアル参照)
8
class SpinlockHle { private: char &lock_; public: explicit SpinlockHle(char &lock) : lock_(lock) { int flags = __ATOMIC_ACQUIRE | __ATOMIC_HLE_ACQUIRE; while (__atomic_exchange_n(&lock_, 1, flags)) _mm_pause(); } ~SpinlockHle() noexcept { int flags = __ATOMIC_RELEASE | __ATOMIC_HLE_RELEASE; __atomic_clear(&lock_, flags); } };
HLE on/off の違い
9
--- t1.hle0.s 2013-08-31 09:23:58.000000000 +0900 +++ t1.hle1.s 2013-08-31 09:23:30.000000000 +0900 @@ -13,12 +13,12 @@ .L3: rep nop .L2: movl %edx, %eax - xchgb -1(%rsp), %al + xacquire xchgb -1(%rsp), %al testb %al, %al jne .L3 - movb $0, -1(%rsp) + xrelease movb $0, -1(%rsp) xorl %eax, %eax ret .cfi_endproc .LFE859:
実験環境
• CPU: Core i7-4770 – 4cores 8HT – HT 有効 – TurboBoost 有効
• メモリ: 16GB • OS: Ubuntu 13.04 x86_64 kernel 3.8.19 • コンパイラ: GCC 4.8.1 – 最適化フラグ: -O2 のみ
10
実験方法
• スレッドを必要なだけ起動して,X 秒間クリティカルセクション (CS) を繰り返し実行
• Spinlock を使って CS の排他を取る – HLE 有効/無効は実験パラメータ – 終了判定のために CS 実行毎に atomic<bool> を load するオーバーヘッドあり
• CS の実行回数の合計値を X で割ったものを スループットとする
• 上記実験を Y 回行い,スループットの avg, min, max を計算
11
Experiments
• Expr1: simple counter(s) • Expr2: counter(s) with delay • Expr3: collision timing • Expr4: access area size • Expr5: map operations
12
Counter
Expr1: simple counter(s)
• クリティカルセクションループ
• パラメータ – 競合率 100%: counter を全スレッドで共有 – 競合率 0%: スレッド毎に counter を持つ
• キャッシュラインが異なるように 64byte 毎に配置
13
Counter
競合率 100% 競合率 0%
Thread Thread
while (!isEnd_.load(std::memory_order_relaxed)) { SpinlockHle<useHLE> lk(mutex_); counter_++; }
Expr1: result
14 10 sec, 20 trials
44.7x
spinlock なしの 8 threads 実行で 4309M なので, isEnd.load() のオーバーヘッドは 8% 程度
Expr1: result (scaled)
15 10 sec, 20 trials
競合100% のときは オーバーヘッドの分 性能悪化
Expr2: counter(s) with delay • 1us delay (誤差は実測 70ns 以下)
• クリティカルセクション
16
void delayUsec(uint64_t usec) { auto ts0 = std::chrono::high_resolution_clock::now(); uint64_t diff = 0; while (diff < usec * 1000) { auto ts1 = std::chrono::high_resolution_clock::now(); diff = std::chrono::duration_cast< std::chrono::nanoseconds>(ts1 - ts0).count(); } }
while (!isEnd_.load(std::memory_order_relaxed)) { SpinlockHle<useHLE> lk(mutex_); counter_++; delayUsec(1); }
Expr2: result
17 10 sec, 20 trials cs が 1us でも最大 5 倍程度の性能アップを期待できる
Expr3:
• 1us delay の前/後にカウンタを更新する
• 目的 – 競合発覚するのが CS の最初か最後かで楽観的実行が失敗する頻度が変化するのを観察したい
18
while (!isEnd_.load(std::memory_order_relaxed)) { SpinlockHle<useHLE> lk(mutex_); if (isBefore) delayUsec(1); counter_++; if (!isBefore) delayUsec(1); }
Expr3: result
19 10 sec, 20 trials
Expr4:
• 目的 – write buffer や read flag を管理する領域は有限なので,HLE で恩恵が受けられるアクセスサイズの上限を知りたい
• 手段 – X 個の 64bytes メモリ断片をスレッド毎に用意 – CS の中で Y 個にアクセスする(重複アリ) – 他の条件を同じにするため,Y は固定 – 2 <= X <= Y で評価 – 今回は write の評価のみ
20
Expr4: 1-4 thread 16 clines
21
1 threads 2 threads
3 threads 4 threads
10 sec, 20 trials CS 内では 12 lines までのアクセスにした方が良さそう
Expr4: 5-8 thread 16 clines
22
5 threads 6 threads
7 threads 8 threads
10 sec, 20 trials 安定しない結果.12 lines を越えても効果があるケースも
Expr4: 5-8 thread 64 clines
23
5 threads 6 threads
7 threads 8 threads
5 sec, 100 trials 安定しない結果,実験方法に問題があるかも?
Expr5:
• 目的: – 実用的なデータ構造で HLE の効果を知る
• 手段: – std::map<uint32_t, uint32_t> をひとつの
spinlock で排他 – read 比率を変える: 0%, 90%, 99%, 100%
• read 操作: ランダムキーで lower_bound 検索 • write 操作: ひとつ削除,その後 insert
– 初期アイテム数: 10K (約2MB), 1M (約100MB) – (ついでに自作の btree map でも試す)
24
20131022追記
• 以下 expr5 の結果グラフのスループットは全て誤って 3 倍に集計されていたことが発覚 – 集計スクリプトのミス
• スループットを見るときは表記の 1/3 にしてご覧ください
25
Expr5: 10K items, read 0%
26
std::map はスレッド数増加で HLE on が逆転 性能上昇は最大で 39% (8 threads)
btree も同様の傾向
Expr5: 10K items, read 90%
27
std::map では 2 threads 以上で HLE on が上回る. 最大46%性能Up (3 threads)
btree では傾向が安定しないが 概ね同程度と言える
Expr5: 10K items, read 99%
28
std::map は最大 2.9 倍の性能 (3 threads)
btree も最大 2.4 倍の性能 (3 threads)
Expr5: 10K items, read 100%
29
std::map は 6 threads で 7.4 倍 btree は 4 threads で 3.4 倍
Expr5: 1M items, read 0%
30
3 threads 以上で HLE on の方が良い std::map 6.9% up (3 threads) btree: 7.3% up (3 threads)
Expr5: 1M items, read 90%
31
std::map は 58% up (3 threads) btree は 54% up (3 threads)
Expr5: 1M items, read 99%
32
std::map は 2.4 倍 (3 threads) btree は 2.4 倍 (3 threads)
Expr5: 1M items read 100%
33
std::map は 2.6 倍 (4 threads) btree は 2.6 倍 (3 threads)
Expr5: まとめ
• 性能向上 – 10K items で 最大 7.4 倍 (read 100%) – 1M items で 最大 2.6 倍 (read 100%)
• 現状の結論 – データがキャッシュに乗る程度に小さく read 比率が低いと HLE のオーバーヘッドが目立つ
• 考察 – critical section でより多くの操作をするケースも評価すべき
34
HLE 評価まとめ
• 手間なしで性能が向上する魔法 – 楽観的挙動 à 悲観的挙動なので 性能最悪値を保証してくれるのも魅力
– デッドロックは従来通り気をつける必要あり
• 使うべき条件 – 条件1: 競合が起きにくい (read 比率が高い) – 条件2: クリティカルセクション実行時間が短い – 条件3: アクセス対象メモリが少ない
35
今後の展望
• HLE には条件分岐予測のように elision すべきかどうかを予測して性能向上させる余地があるのではないか? – 今回あまり調べてないので既にやってたらごめんなさい
• RTMは? – L1 のみならず,L2/L3 そしてメモリコントローラまで
TM のことを考えてくれるようになるまで様子見したい – 速度が重要じゃない用途なら STM と連動できるようになった時点で開発効率の点から有用なのではないか
36
おまけ: 私の単体 CPU 購入歴
• AMD K6-300 • Intel Pentium II 333MHz • AMD Athlon 64 3200+ • AMD Athlon X2 BE-2400 • AMD Phenom II X4 910e • AMD Phenom II X6 1065T
• こんな私が買う気になってるのだから Haswell は凄い!
37
実験してみたい人へ
• 用意するもの – TSX サポート付きの Haswell CPU – Linux OS (古いものはオススメしない) – GCC 4.8 以降
• ソースコード – https://github.com/starpos/hle_bench
38
ありがとうございました
• ご質問,コメントはご気軽にどうぞ J
39