Mercurial > hg > Papers > 2014 > toma-master
view slides/master.html @ 66:7c7afe38c9d6
rewrite slides
author | Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Thu, 13 Feb 2014 03:02:48 +0900 |
parents | 6fbc1771d2be |
children | 99cd59a56524 |
line wrap: on
line source
<!DOCTYPE html> <!-- Google HTML5 slide template Authors: Luke Mahé (code) Marcin Wichary (code and design) Dominic Mazzoni (browser compatibility) Charles Chen (ChromeVox support) URL: http://code.google.com/p/html5slides/ --> <html> <head> <title> 関数型言語 Haskell による並列データベースの実装 </title> <meta charset='utf-8'> <script src='http://web.amothic.com/html5slides/slides.js'></script> </head> <style> /* Your individual styles here, or just use inline styles if that’s what you want. */ </style> <body style='display: none'> <section class='slides layout-regular template-concurrency'> <!-- Your slides (<article>s) go here. Delete or comment out the slides below. --> <article> <h1> 関数型言語 Haskell による並列データベースの実装 </h1> <p> Daichi TOMA <br> Feb 12, 2014 </p> </article> <article> <h3> 研究概要 </h3> <p> Haskell を用いて並列データベースを実装した </p> <p> 読み込みに関して 12 コアで実行した場合、10.37 倍 という性能向上率が確認できた </p> <p> また、Web 掲示板サービスを開発し、Java と比較して読み込みで 3.25 倍、書き込みで 3.78 倍の性能差が確認できた </p> </article> <article> <h3> 研究背景 </h3> <p> Web サービスの脆弱性を悪用されると多大な被害がでる </p> <p> Haskell は型検査でバッファオーバーフローや、クロスサイトスクリプティング、SQL インジェクションを防げる </p> <p> Haskell を用いることで信頼性の高いデータベースとWebサービスを開発できる </p> </article> <article> <h3> 型による区別 </h3> <p> Haskellは、全てに型がある </p> <p> 文字列やHTMLを別の型として扱う </p> <p> 入力として受け取った文字列が、HTML型に自動変換されたりせず、脆弱性が勝手に入り込んだりしない </p> </article> <article> <h3> 実行時型エラーがない </h3> <p> Haskellは、実行時に型に起因するエラーが起きない </p> <p> [1,2,3]のリストに文字'a'を追加することや、['a','b','c']のリストに 1 を追加することはできない </p> <p> コンパイル時にエラーになる </p> <pre> abc = 'a' : [1,2,3] -- error cde = 1 : ['a','b','c'] -- error </pre> </article> <article> <h3> 型推論 </h3> <p> 型エラーを検出するために全ての式や関数に型を書いていくのは大変 </p> <p> Haskell は 型を推論してくれる </p> </article> <article> <h3> 型推論 </h3> <p> 前提として関数に以下のような型が定義されている </p> <pre> getNode :: Node -> Path -> Node elems :: Map k a -> [a] children :: Node -> Map Int Node </pre> <p> 新しくgetChildrenという関数を定義するとき型を自動で導出できる </p> <pre> getChildren node path = elems (children (getNode node path)) </pre> <p> Haskell が推論した型を確認する </p> <pre> *Jungle> :type getChildren getChildren :: Node -> Path -> [Node] </pre> </article> <article> <h3> 実用的なデータベースの開発 </h3> <p> 現在、CPU はマルチコア化がすすんでいる </p> <p> 実用的なデータベースとするためにはマルチコアに対応させる必要性がある </p> <p> 並列に処理できればマルチコアで性能が出る </p> </article> <article> <h3> Haskell の並列実行 </h3> <p> Haskell で並列実行するにはどうするか </p> </article> <article> <h3> 非破壊的木構造 </h3> <p> マルチコア上で、データベースの性能向上をさせるためには、各コアからデータに同時にアクセスできるようにする </p> <p> 非破壊的木構造という手法を用いる。 非破壊的木構造は、元となる木構造を書き換えずに編集できる。 </p> <p> 既にあるデータを変更しないので、データの競合状態が発生しない。並列に読み書きできる </p> <br> <div align="center"> <img src="images/concurrent_edit.png" width="400px"> </div> </article> <article> <h3> 非破壊的木構造 - ルートノード </h3> <p> 非破壊的木構造の並列実行のネックとなるものがルートノード </p> <p> ルートノードは、どの木構造が最新なのかを表す情報 </p> <p> 状態を持つのはここだけで、並列度を高めるにはルートノードの設計が重要 </p> <div align="center"> <img src="images/rootnode.png" width="350px"> </div> </article> <article> <h3> Haskell でのルートノードの管理 </h3> <p> ソフトウェア・トランザクショナル・メモリ (STM) を使う </p> <p> STM は、ノンブロッキングで共有データを扱える </p> <p> 共有データの変更は以下の様に行われる </p> <ul> <li>他から変更がなければそのまま適用 <li>変更している間に、他から変更されれば変更処理をやり直し </ul> </article> <article> <h3> STM のメリット </h3> <p> 共有データにアクセスする時に待ちがない </p> <p> 読み込みは他の変更を気にせず並列に行える </p> </article> <article> <h3> 非破壊的木構造データベース Jungle </h3> <p> 非破壊的木構造を扱うデータベース Jungle を開発した </p> <p> Jungle の基本的な使い方 </p> <ul> <li> 木構造を保持する Jungle を作成 <li> Jungle に新しい木を作成 <li> 木から最新のルートノードを取ってきて、データを参照する <li> もしくは、ノードを編集し、木の最新のルートノードを更新する </ul> </article> <article> <h3> 木構造を保持する Jungle の作成 </h3> <pre> data Jungle = Jungle (TVar (Map String Tree)) </pre> <p> TVarがついているのはSTMを使っている変数 </p> <p> 各スレッドから新しく木を作ったりできるように木構造の保持にもSTMを使っている </p> <p> Jungle は複数の Tree を名前で管理する </p> <div align="center"> <img src="images/jungle_type.png" width="500px"> </div> </article> <article> <h3> Jungle に新しい木を作成 </h3> <pre> data Tree = Tree (TVar Node) String </pre> <p> Tree は最新のルートノードの情報と木の名前を持っている </p> <br> <br> <div align="center"> <img src="images/get_root_node.png" width="500px"> </div> </article> <article> <h3> 木から最新のルートノードを取ってきて、データを参照する </h3> <p> 木から最新のルートノードを取ってくるにはgetRootNodeという関数を使う </p> <pre> getRootNode :: Jungle -> String -> IO Node </pre> <p> Jungleと木の名前を渡すと最新のルートノードが返ってくる </p> </article> <article> <h3> ノード </h3> <p> Jungle で最も基本的な構成要素 </p> <p> Jungle の参照や変更の関数は全てノードに対して行う </p> <pre> data Node = Node (Map Int Node) (Map String ByteString) </pre> <div align="center"> <img src="images/node_component.png" width="500px"> </div> </article> <article> <h3> ノードを編集し、木の最新のルートノードを更新する </h3> <p> updateRootNodeやupdateRootNodeWithで更新できる </p> <pre> updateRootNode :: Jungle -> String -> Node -> IO () updateRootNodeWith :: (Node -> Node) -> Jungle -> String -> IO () </pre> <p> updateRootNodeには編集したNodeを渡すことで木の最新のルートノードを更新できる </p> <p> updateRootNodeWithにはNodeを編集する関数を渡すことで木の最新のルートノードを更新できる </p> </article> <article> <h3> 性能計測 </h3> <p> Jungle がマルチコアプロセッサで性能が出るのか、実用的なWebサービスが提供できるのか確認する </p> <p> 性能の計測に用いるサーバの仕様 </p> ハイパースレッディングで24コアまで使える<br> ハイパースレッディングはおよそ20%程度クロックあたりの性能が向上する </p> <table> <tr> <th>名前</th> <th>概要</th> </tr> <tr> <td>CPU</td> <td>Intel(R) Xeon(R) CPU X5650@2.67GHz * 2</td> </tr> <tr> <td>コア数</td> <td>12</td> </tr> <tr> <td>メインメモリ</td> <td>126 GB</td> </tr> <tr> <td>OS</td> <td>Fedora 19</td> </tr> </table> </article> <article> <h3> Haskell コンパイラ </h3> <p> Haskell のコンパイラには The Glasgow Haskell Compiler (GHC) を利用する </p> <p> 現在の GHC の安定版 7.6.3 は並列時にIOマネージャーがスケールしないという問題がある </p> <p> リリース候補版である 7.8 を用いる </p> </article> <article> <h3> 親和性機能 </h3> <p> 親和性機能とは OS スレッドをCPUコアに固定する機能のこと </p> <p> 並列実行時の性能が向上するため性能計測で利用する </p> <p> Haskell は実行時に -qa オプションを使うこと親和性機能を使うように指示できる </p> </article> <article class="smaller"> <h3> 読み込みの性能計測 </h3> <p> 木構造の読み込みにかかる時間を計測する </p> <p> 12 スレッドで親和性機能を使って実行した場合 10.37 倍の性能向上 </p> <p> 親和性機能を使って24スレッドで実行すると実行時間が大幅に伸びる </p> <table> <tr> <th>CPU数</th> <th>親和性機能なし</th> <th>親和性機能あり</th> </tr> <tr> <td>1</td> <td>60.95 s</td> <td>61.00 s</td> </tr> <tr> <td>2</td> <td>30.83 s</td> <td>33.95 s</td> </tr> <tr> <td>4</td> <td>15.49 s</td> <td>16.10 s</td> </tr> <tr> <td>8</td> <td>10.31 s</td> <td>8.79 s</td> </tr> <tr> <td>12</td> <td>8.49 s</td> <td>5.88 s</td> </tr> <tr> <td>16</td> <td>5.82 s</td> <td>5.81 s</td> </tr> <tr> <td>20</td> <td>6.54 s</td> <td>5.48 s</td> </tr> <tr> <td>24</td> <td>8.21 s</td> <td>125.09 s</td> </tr> </table> </p> </article> <article class='nobackground'> <h3> 読み込みの性能計測 </h3> <p> 親和性機能を使った場合、実コアの12スレッドまでほぼ線形にスケールする </p> <div align="center"> <img src="images/read.png" width="700px"> </div> </article> <article class="smaller"> <h3> 書き込みの性能計測 </h3> <p> 木構造の書き込みにかかる時間を計測する </p> <p> 12 スレッドで親和性機能を使って実行した場合 3.82 倍の性能向上 </p> <table> <tr> <th>CPU数</th> <th>親和性機能なし</th> <th>親和性機能あり</th> </tr> <tr> <td>1</td> <td>49.70 s</td> <td>49.61 s</td> </tr> <tr> <td>2</td> <td>27.77 s</td> <td>30.76 s</td> </tr> <tr> <td>4</td> <td>18.06 s</td> <td>18.05 s</td> </tr> <tr> <td>8</td> <td>16.66 s</td> <td>12.50 s</td> </tr> <tr> <td>12</td> <td>15.62 s</td> <td>12.96 s</td> </tr> <tr> <td>16</td> <td>14.91 s</td> <td>13.11 s</td> </tr> <tr> <td>20</td> <td>15.31 s</td> <td>13.84 s</td> </tr> <tr> <td>24</td> <td>18.11 s</td> <td>71.66 s</td> </tr> </table> </p> </article> <article class='nobackground'> <h3> 書き込みの性能計測 </h3> <p> 4 倍程度で性能向上が頭打ちになっている </p> <p> 同時に書き込みがあった場合、STMが処理をやり直すため並列度が下がる </p> <div align="center"> <img src="images/write.png" width="700px"> </div> </article> <article> <h3> 考察 </h3> <p> 読み込みが高速 </p> <p> 書き込みより読み込みが多用されるシステムに向いている </p> </article> <article> <h3> Webサービスに組み込んでの性能計測 </h3> <p> Haskell の HTTP サーバ Warp と組み合わせて Web掲示板サービスを開発する </p> <p> 測定ツール weighttp を用いて掲示板に読み込みと書き込みで負荷をかける。 </p> <p> Warp は、ハイパースレッディングで明らかに遅くなるので、使わない </p> </article> <article class="nobackground"> <h3> ネットワークのボトルネック </h3> <p> ネットワークのボトルネックが大きい </p> <p> 並列環境でどのようにスケールするか計測したいため、ネットワークを介さずに性能計測を行う </p> <br> <div align="center"> <img src="images/warp.png" width="600px"> </div> </article> <article> <h3> 実験環境 </h3> <p> 3 コアを測定ツール weighttp に使い、 8 コアを Web 掲示板サービスに使う </p> <br> <div align="center"> <img src="images/request.png" width="400px"> </div> </article> <article> <h3> Webサービスに組み込んでの性能計測 </h3> <p> 読み込みと書き込みの実験結果 </p> <p> 1秒間あたりどれだけリクエストを捌けるかという指標で比較 大きければ大きいほど性能が良い </p> <p> 8 スレッドで実行時、読み込みは 6.18 倍、書き込みは3.93倍の性能向上 </p> <table> <tr> <th>CPU数</th> <th>読み込み</th> <th>書き込み</th> </tr> <tr> <td>1</td> <td>22,624 req/s</td> <td>28,552 req/s</td> </tr> <tr> <td>2</td> <td>43,083 req/s</td> <td>53,765 req/s</td> </tr> <tr> <td>4</td> <td>92,548 req/s</td> <td>98,691 req/s</td> </tr> <tr> <td>6</td> <td>119,310 req/s</td> <td>99,009 req/s</td> </tr> <tr> <td>8</td> <td>139,965 req/s</td> <td>112,212 req/s</td> </tr> </table> </article> <article class='nobackground'> <h3> Webサービスに組み込んでの性能計測 </h3> <p> Jungle 単体での実験結果と同じで、読み込みのほうがスケールする </p> <div align="center"> <img src="images/bbs.png" width="700px"> </div> </article> <article> <h3> Java との比較 </h3> <p> HaskellとJavaで同様のWeb掲示板サービスを用意する </p> <p> 実環境を想定して、ブレードサーバ2台用意し、ネットワークを介して負荷をかける </p> <p> 100 万リクエストを処理するのにかかる時間を計測 </p> <table> <tr> <th>測定</th> <th>Haskell</th> <th>Java</th> </tr> <tr> <td>読み込み</td> <td>16.31 s</td> <td>53.13 s</td> </tr> <tr> <td>書き込み</td> <td>20.17 s</td> <td>76.4 s</td> </tr> </table> <p> 読み込みで 3.25 倍、書き込みで 3.78 倍の性能差 </p> <p> Haskell は実用的なWebサービスを開発できる </p> </article> <article> <h3> Java との比較 - 生産性 </h3> <p> 生産性の面からも Java との比較を行う </p> <p> Haskell 版 Jungle は 284 行、 Java 版 Jungle は 3,390 行 </p> <p> 実装が 1/12 程度のサイズとなっている </p> <p> 再帰的なデータ型の定義を言語としてサポートしていることや、関数の再利用が行いやすいことが要因 </p> <p> 同じ機能を実装する場合でも、Haskell は Java と比較してコード行数が短くなる </p> </article> <article> <h3> まとめ </h3> <p> 純粋関数型言語 Haskell を用いて並列データベースの実装をおこなった </p> <p> 読み込みに関して 12 コアで実行した場合、10.37 倍 という性能向上率が確認できた </p> <p> また、Web 掲示板サービスを開発し、Java と比較して読み込みで 3.25 倍、書き込みで 3.78 倍の性能が確認できた </p> </article> <article> <h3> 今後の課題 </h3> <p> 書き込み処理の性能向上 </p> <p> 分散データベースとしての実装 </p> <p> 永続性の実装 </p> </article> </body> </html>