Upgrade to Pro — share decks privately, control downloads, hide ads and more …

サーバサイドの並行プログラミング〜かんたんマルチスレッドプログラミング〜

 サーバサイドの並行プログラミング〜かんたんマルチスレッドプログラミング〜

GREE Tech Talk #05での発表資料です。
http://techtalk5.labs.gree.jp/

gree_tech

May 19, 2014
Tweet

More Decks by gree_tech

Other Decks in Technology

Transcript

  1. Copyright © GREE, Inc. All Rights Reserved. • GREEにおける並列・並⾏ •

    並⾏プログラミングの利点・⽋点 • マルチスレッドの難しさ • かんたんマルチスレッドプログラミング • 例︓ゲートウェイサーバの実装 • 性能評価 • まとめ ⽬次
  2. Copyright © GREE, Inc. All Rights Reserved. Webアプリケーション • 3層アーキテクチャ

    + CDN • フロントエンド(Nginx、Varnish など) • アプリケーションサーバ(Apache+PHP など) • データベース(MySQL など) • アプリケーションサーバは状態をもたない • HTTPのリクエストが終了したら終わり • 共有される情報はすべてDBに格納されている 問題の単純化︓状態を分離 • どうやって整合性を保つか → 同じデータを同時に扱わない → データベースが競合を解決してくれる GREEにおける並⾏・並列 ⼤規模Webサイトにおける並⾏処理
  3. Copyright © GREE, Inc. All Rights Reserved. リレーショナルDBの場合 • トランザクション分離レベルさえ気にすれば良い

    • SERIALIZABLE • REPEATABLE READ • READ COMMITTED • READ UNCOMMITTED KVSの場合 • 条件付き書き込み命令を使⽤する • Ex.) CAS命令 (Memcached / Flare) • 複数命令をまとめて処理する機能を使⽤する • Ex.) Multi … Exec命令 (Redis) GREEにおける並⾏・並列 ⼤規模Webサイトにおける並⾏処理 (cont.)
  4. Copyright © GREE, Inc. All Rights Reserved. マルチプレイヤー型オンラインゲーム • クライアントは常にサーバに接続される

    • 複数のクライアント間で状態を共有 • DB上の状態もあれば、メモリ上にのみ存在する状態もある 問題の単純化︓プロセスに分割 • ワールドなどの単位でプロセスを分割 • 各クライアントの処理をちょっとずつ進める(イベント駆動) パフォーマンスが求められる場合 • マルチプロセッサを活かすためにマルチスレッド化 • 並⾏プログラミング︕ GREEにおける並⾏・並列 マルチプレイヤー型オンラインゲーム
  5. Copyright © GREE, Inc. All Rights Reserved. マルチスレッドを使ったサーバミドルウェア OSSになっているもの •

    Flare (2008) • 分散KVS • https://github.com/gree/flare • PrimDNS (2011) • ⾼性能DNSサーバ • https://github.com/ebisawa/primdns 要件 • 多数のクライアントからの要求を処理できる • 数百〜数千台のWebサーバから接続が来る • マルチプロセッササーバでスケールする • 1サーバに10~20程度の論理コア GREEにおける並⾏・並列 ⾃社開発ミドルウェア
  6. Copyright © GREE, Inc. All Rights Reserved. 各種KPIの算出 • ⼤量の情報の中から、欲しい時に素早く情報を提供する

    • 並列分散処理ミドルウェアを活⽤ • Hadoop, Hive, Presto … GREEにおける並⾏・並列 ⼤量データの処理
  7. Copyright © GREE, Inc. All Rights Reserved. サーバサイドで並⾏プログラミングをする理由 • 多数のクライアントから同時に要求される処理をこなす

    • クライアント間で共有すべき状態が存在する よくある実現⽅法 • 各クライアント毎にスレッドを実⾏する • ロックと条件変数を使って整合性を担保する • 1つのスレッドで各クライアントの処理を少しずつ進める • イベントとコールバックを駆使する (ex. Redis, node.js) 並⾏プログラミングの⽅法 マルチスレッドとイベント駆動
  8. Copyright © GREE, Inc. All Rights Reserved. 各クライアント毎にスレッドを実⾏する • 利点︓処理を順次書いていけばよい

    • ⽋点︓スレッド間の競合を意識する必要がある 1つのスレッドで各クライアントの処理を少しずつ進める • 利点︓メモリアクセスの排他制御の必要がない • ⽋点︓処理を細切れに書くため、⾒通しが悪い マルチプロセッサを活⽤できない いずれの⽅法をとるにせよ、プログラミングは難しい 参考: 「マルチコア時代の サーバプログラミング と Haskell」(⼭本和彦さん) • http://www.iij.ad.jp/company/development/tech/techweek/pdf/tw2011_04_haskell.pdf 並⾏プログラミングの⽅法 利点と⽋点
  9. Copyright © GREE, Inc. All Rights Reserved. 過去数年間マルチスレッドサーバを運⽤してきましたが・・ • 再現させるのが難しい

    • 「ある特定の⾏と⾏の間でスレッドが切り替わると起こるかもしれない」 • 「42億回クライアントが接続した後に起こるかもしれない」 • 「複数のサーバーが同時に故障すると起こるかもしれない」 • 修正が正しいかの検証が難しい • 複数スレッドの進⾏状況をすべて考慮する必要がある。 考慮したところで、⼈間の考えることは信⽤ならない。 • 不安なら全状態をツールでチェックすればよいが、状態のモデル化が⼤変。 モデルは⼈間が作るのでまちがってる可能性もある。 • 解決⽅法=枯れるのを待つ • もぐらたたきの結果、安定したソフトウェアができあがる マルチスレッドプログラミングは難しい サーバ運⽤の観点からみた難しさ
  10. Copyright © GREE, Inc. All Rights Reserved. 実例 • Memcached

    1.4.6 のバグ • 複数のMemcachedが同時に落ちる • mixi.jp でアクセス障害(2010/8) • http://mixi.co.jp/press/2010/0812/3477/ • 詳細はmixiさんのエンジニアブログに書いてあります • http://alpha.mixi.co.jp/2011/10745/ 教訓(個⼈的感想) • 先⼈は偉⼤である • マルチスレッドプログラミングはやはり難しい マルチスレッドプログラミングは難しい サーバ運⽤の観点からみた難しさ(cont.)
  11. Copyright © GREE, Inc. All Rights Reserved. “Why Threads Are

    A Bad Idea (for most purposes)” by John Ousterhout (1995) マルチスレッドプログラミングは難しい 20年前から⾔われ続けている・・
  12. Copyright © GREE, Inc. All Rights Reserved. 30年の研究を経て、 共有メモリを⽤いたタスクレベルの並⾏処理で、 最も利⽤されている協調の仕組みは....

    マルチスレッドプログラミングは難しい 結局なにがいけないのか mutex_lock() / cond_wait() (;´Д`) こ、こんな古い物を・・・ * http://research.microsoft.com/en-us/um/people/simonpj/papers/stm/STMTokyoApr10-Japanese.pdf • 並⾏プログラミングを容易にするいろいろな⼿法が過去発明されてき たにもかかわらず、それを利⽤することができていない。 ロックと条件変数(30年前の発明) * by Simon Peyton Jones
  13. Copyright © GREE, Inc. All Rights Reserved. Webアプリケーションはいかにして複雑さを回避していたか︖ • 状態の保持を専⽤の機構(=データベース)に委譲

    • データベースの提供するトランザクションを利⽤ マルチスレッドプログラミングは難しい 結局なにがいけないのか(cont.) ( ゚д゚)ハッ! | \ __ / _ (m) _ピコーン |ミ| / .`´ \ ∧_∧ / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ (・∀・∩< DBつかえばいいんじゃね (つ 丿 \_________ ⊂_ ノ (_)
  14. Copyright © GREE, Inc. All Rights Reserved. 我々はソフトウェアトランザクショナルメモリ(STM)を使っています STM •

    アトミックブロック内の⼀貫性保持 • プロック内では、あたかも⾃分のスレッドだけが動いているようにふるまう (SERIALIZABLEと同じ) • ロールバック • 処理の途中で変数への書き込みをなかったことにできる かんたんマルチスレッドプログラミング 並⾏プログラミングの容易化
  15. Copyright © GREE, Inc. All Rights Reserved. 実装の歴史 • GHC

    6.4 (Haskell) − 2005年 • https://hackage.haskell.org/package/stm • Scala STM – 2010年 • http://nbronson.github.io/scala-stm/ • GCC 4.7 (C/C++) − 2012年 • http://gcc.gnu.org/wiki/TransactionalMemory (experimental) ほとんどの主要⾔語になんらかの実装があるようです • http://en.wikipedia.org/wiki/Software_transactional_memory#Implementations かんたんマルチスレッドプログラミング 実は普及しているSTM
  16. Copyright © GREE, Inc. All Rights Reserved. “Transactional Variable(TVar)”で状態を表現 •

    Intを保持したいなら TVar Int という型を使う • アトミックブロック内でのみ操作できる 動作 • とりあえず実⾏してみる • ⾃分がTVarを参照している最中に他の⼈がそれを 書き換えたら、処理をやり直す 特徴 • デッドロックの問題は回避できるが、 平等性(fairness)の問題がある • 処理を合成でき、合成してもSTMの性質を保つ • 現在の実装ではロックを使⽤しているっぽい HaskellのSTM 本発表から聴講された⽅向け 10章あたりにかいてあります
  17. Copyright © GREE, Inc. All Rights Reserved. • atomically −

    STMの実⾏を開始する • readTVar − TVarから値を読む • writeTVar − TVarに値を書き込む • retry − TVarが更新されるまで待って、処理をやり直す HaskellのSTM STMの使い⽅ main = do tvar <- newTVarIO "ガッ" forkIO $ forever $ atomically $ do x <- readTVar tvar if x == "ガッ" then writeTVar tvar "ぬるぽ" else retry forever $ atomically $ do x <- readTVar tvar if x == "ぬるぽ" then writeTVar tvar "ガッ" else retry
  18. Copyright © GREE, Inc. All Rights Reserved. • STMは合成可能 •

    STMの中では、任意のSTM関数 を呼び出して良い • TVarをプリミティブとして、 より複雑なデータ構造を構築 • Ex. TQueue • 例外でロールバックされる • 途中の状態は他から⾒えない • 「スレッドが例外で終了して状態 がおかしくなる」問題がない • ex. キューからアイテムを取り出 して別のキューにいれる HaskellのSTM STMはなにがすごいか Control.Concurrent.STM.TQueueの実装
  19. Copyright © GREE, Inc. All Rights Reserved. MVar • 1つものをいれられる箱

    • putMVar関数 - もうすでに⼊ってたら、空くまで実⾏が停⽌する • takeMVar関数 – まだ⼊ってなければ、⼊るまで実⾏が停⽌する • 特徴 • スレッド間の制御に使える • 誤って使うとデッドロックする可能性がある IORef • アトミックに書き換え可能 • atomicModifyIORef関数 • 扱う変数が1個ならこちらを使ったほうが速い HaskellのSTM 他にもあるHaskellの状態保持機構
  20. Copyright © GREE, Inc. All Rights Reserved. • システム構成 •

    ZooKeeper • Frontend サーバ • Control サーバ • Flare Indexサーバ • Flare ノード STMの使⽤例 KVS⾃動運⽤システム Zookeeper Zookeeper Control Control Control Control Zookeeper Frontend Frontend Frontend Frontend Flare Index Flare Index Flare Index Web Servers Web Servers Web Servers Flare Flare Flare ノードの監視・制御 構築・管理 KVS⾃動運⽤システム アプリケーション 構築・管理 get/set get/set
  21. Copyright © GREE, Inc. All Rights Reserved. ゲートウェイサーバ (Frontend) ゲートウェイサーバ

    (Frontend) ゲートウェイサーバ (Frontend) • リクエスト処理スレッドと転送処理スレッドに分割 • それぞれのスレッドがキュー(TQueue)を介して通信 • スレッド+プロセス多重 • 性能と安全性を考慮してプロセス多重も併⽤ STMの使⽤例 ゲートウェイサーバの実装 リクエスト スレッド 転送処理 スレッド 転送処理 スレッド 転送処理 スレッド リクエスト スレッド リクエスト スレッド リクエスト スレッド リクエスト 受付キュー 応答受取 キュー Response Request Flare サーバ Flare サーバ Flare サーバ 転送処理 スレッド 転送 スレッド 転送処理 スレッド 転送 スレッド 転送処理 スレッド 転送 スレッド クライ アント クライ アント クライ アント クライ アント
  22. Copyright © GREE, Inc. All Rights Reserved. 転送性能を評価 • C++で書かれたサーバ

    • Flareのプロキシモード(マルチスレッド) • Haskellで書かれたサーバ • GHC7.8向けに修正したFrontend 1) レイテンシの計測 単⼀スレッドでリクエストを実⾏し、リクエスト毎の応答時間を計測 2) スループットの計測 複数スレッドでリクエストを実⾏し、処理したリクエストの数を計測 ゲートウェイサーバの性能評価 測定対象および項⽬ KVSサーバ (Flare) プロキシ(C++) プロキシ(Haskell) ベンチマーク プログラム 構成
  23. Copyright © GREE, Inc. All Rights Reserved. 構成 Intel(R) Xeon(R)

    CPU E5-1650 @ 3.20GHz(HTと省電⼒機能を無効化) 1台のPCで測定 測定結果 ほぼ互⾓ ゲートウェイサーバの性能評価 レイテンシ
  24. Copyright © GREE, Inc. All Rights Reserved. 構成 • Intel(R)

    Xeon(R) CPU E5-1650 @ 3.20GHz(HTと省電⼒機能を無効化) • 1台のPCで測定 測定結果 • C++で書いたサーバの80%程度 • 要チューニング︖ ゲートウェイサーバの性能評価 スループット(マルチスレッド) 0 5000 10000 15000 20000 25000 30000 35000 40000 C++/mt Haskell/mt set(qps) set(qps) 0 5000 10000 15000 20000 25000 30000 35000 40000 45000 C++/mt Haskell/mt get(qps) get(qps)
  25. Copyright © GREE, Inc. All Rights Reserved. 構成 • マルチプロセスで起動(現在使っている⽅式)

    • 独⽴したサーバ(16コア)でgetコマンドの性能を測定 • 64, 128, 256並列 測定結果 • CPUバウンドではないのであまり性能差はない ゲートウェイサーバの性能評価 [参考] スループット(マルチプロセス) 0 10000 20000 30000 40000 50000 60000 C++/mt Haskell/prefork 64 128 256
  26. Copyright © GREE, Inc. All Rights Reserved. • イベントログを threadscope

    で可視化 • GHCのランタイムがスレッド関連のイベントを出⼒してくれる プログラムの最適化 threadscope
  27. Copyright © GREE, Inc. All Rights Reserved. • マルチスレッドプログラミングは難しい。 •

    ロックと条件変数のせいかもしれません。 まとめ 本⽇の結論 • STMを使うと、簡単にマルチスレッドのサーバが作れます。 • 性能はそこそこ良いです。 • STMを使うなら、Haskellがお勧めです。 • プロダクションで使われています。 • Haskellを使って、サーバを書きましょう︕ • ⽣産性があがります。