view slides/master.html @ 75:fcc71ca8dac7

fix
author Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
date Thu, 13 Feb 2014 14:42:12 +0900
parents 68e21fa11919
children
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> Haskell は rpar により並列実行を指示し、STMにより共有データを実現する</p>
        <p> Haskellの並列処理は、最新のコンパイラで性能がでることがわかった</p>
        <p> 読み込みに関して 12 コアで実行した場合、10.37 倍 という性能向上率が確認できた </p>
        <p> Web 掲示板サービスを開発し、Java と比較して読み込みで 3.25 倍、書き込みで 3.78 倍の性能差が確認できた </p>
        <p> Network の並列性能はあまり出てないことがわかった</p>
</article>

<article>
        <h3> Haskellによる信頼性の高いサービスの構築</h3>
        <p> Web サービスの脆弱性を悪用されると多大な被害がでる </p>
        <p> 脆弱性の大半はBuffer Overflowや、Cross site scripting、SQL injection </p>
        <p> これらは型エラー。Haskell は、これらをコンパイル時にチェック</p>
        <p> 入力として受け取った文字列が、HTML型に実行時に自動変換されたりしない<br>
         ⇒ Cross site scripting が防げる </p>
</article>

<article>
        <h3> Haskellの型検査の例(list) </h3>
        <p> : はlistに要素を追加するための演算子 </p>
        <p> 1 : 2 : 3 : [] == [1,2,3] </p>
        <br>
        <div align="center">
          <img src="images/list.png" width="200px">
        </div>
<pre>
let a =  0  : [1,2,3] -- [0,1,2,3]
let b = 'a' : [1,2,3] -- error
let c =  1  : ['a','b','c'] -- error
</pre>
</article>

<article>
        <h3> 型推論 </h3>
        <p> 前提として関数に以下のような型が定義されているとする </p>
<pre>
getNode :: Node -&gt; Path -&gt; Node
elems :: Map k a -&gt; [a]
children :: Node -&gt; Map Int Node
</pre>
        <p> 新しくgetChildrenという関数を定義する</p>
<pre>
getChildren node path = elems (children (getNode node path))
</pre>
        <p> 型をHaskellが自動で導出する </p>
<pre>
*Jungle&gt; :type getChildren
getChildren :: Node -&gt; Path -&gt; [Node]
</pre>
</article>

<article>
	<h3> 実用的なデータベースには並列実行が必須 </h3>
        <p> 現在、CPU はマルチコア化がすすんでいる </p>
        <p> 実用的なデータベースとするためにはマルチコアに対応させる必要性がある </p>
        <p> 並列に処理できればマルチコアで性能が出る </p>
        <p> Haskell は Eval モナド(rpar)といったモナドで並列実行の機能を提供</p>
        <p> モナドは、Haskellでメタ計算を提供する構文あるいはAPI</p>
        <p> システムコールのようなものと考えてよい</p>
        <p> モナドの例:IOモナド、エラーモナド、STMモナド </p>
</article>

<article>
				<h3> Haskell の並列処理 </h3>
        <p> sumという1つ目の引数から2つ目の引数までの全ての数を足す関数があるとする </p>
        <p> rpar の引数にすることで関数が並列に実行可能であることを示せる </p>
        <p> do ~ returnで rpar を 1 つにまとめ、runEvalに渡すことで実行する </p>
<pre>
main = print (runEval test)

test = do
       a &lt;- rpar (sum 0     10000)
       b &lt;- rpar (sum 10000 20000)
       return (a,b)
</pre>
        <p> a と b が並列に計算される</p>
</article>

<article>
				<h3> Haskell の遅延評価 </h3>
        <p> 並列実行は遅延評価が問題になる </p>
        <p> haskell では値が必要となるまで式の評価が行われない </p>
        <p> 並列に実行するように指示しても、評価が行われなければ並列に動かない </p>
        <p> さきほどの例ではprintを行うようにしていた</p>
<pre>
main = print (runEval test)
</pre>
</article>

<article>
				<h3> Haskell の遅延評価 </h3>
        <p> sprintは評価を行わずに値を表示するコマンド </p>
        <p> _ は未評価の式を表し、thunk と呼ばれる </p>
<pre>
ghci&gt; let y = map (+1) [1,2,3] :: [Int]
ghci&gt; :sprint y
y = _
ghci&gt; length y
3
ghci&gt; :sprint y
y = [_,_,_]
ghci&gt; head y
2
ghci&gt; :sprint y
y = [2,_,_]
</pre>
        <p> 並列実行時には出力などを挟んで強制的に即時評価されるようにする </p>
</article>

<article>
				<h3> データベースの設計 - 非破壊的木構造 </h3>
        <p> マルチコア上で、データベースの性能向上をさせるためには、各コアからデータに同時にアクセスできるようにする </p>
        <p> 非破壊的木構造という手法を用いる。</p> 
        <p> 非破壊的木構造は、元となる木構造を書き換えずに編集できる。 </p>
        <p> 既にあるデータを変更しないので、データの競合状態が発生しない。並列に読み書きできる </p>
        <br>
        <div align="center">
          <img src="images/concurrent_edit.png" width="400px">
        </div>
			</article>

<article>
        <h3> Haskell でのRoot Node の管理 </h3>

        <p> Root Node は最新のNode → 状態を持つ</p>
        <p> 共有された状態を作るには Software Transactional Memory (STM) を使う </p>
        <p> STM は、Non-Blockingで共有データを扱える </p>
        <p> 共有データの変更は以下の様に行われる </p>
        <ul>
          <li>他から変更がなければそのまま適用
          <li>変更している間に、他から変更されれば変更処理をやり直し
        </ul>
</article>

<article>
				<h3> 非破壊的木構造データベース Jungle </h3>
        <p> 非破壊的木構造を扱うデータベース Jungle を開発した </p>
        <p> Jungle の基本的な使い方 </p>
        <ul>
          <li> 木構造を保持する Jungle を作成
          <li> Jungle に新しい木を作成
          <li> 木からRoot Nodeを取ってきて、データを参照
          <li> もしくは、Node を編集し、木のRoot Nodeを更新
        </ul>
</article>

<article>
				<h3> 木構造を保持する Jungle の作成 </h3>
        <p> 各スレッドから新しく木を作ったりできるように木構造の保持にSTMを使っている </p>
        <p> Jungle は複数の Tree を名前で管理する </p>
        <div align="center">
          <img src="images/jungle_type.png" width="500px">
        </div>
</article>

<article>
				<h3> 木からRoot Nodeを取ってくる </h3>
        <p> 木からRoot Nodeを取ってくるにはgetRootNodeという関数を使う </p>
        <p> 内部でSTMを使っているのでIOモナドを返す</p>
<pre>
getRootNode :: Jungle -&gt; String -&gt; IO Node
</pre>
        <p> Jungleと木の名前を渡すと最新の Node が返ってくる </p>
</article>

<article>
        <h3> Jungle の最も基本的なデータ構造 Node </h3>
        <p> Node はChildren(Node)とattributesを持つ </p>
        <p> Jungle の参照や変更の関数は全て Node に対して行う </p>
        <div align="center">
          <img src="images/node_component.png" width="500px">
        </div>
</article>

<article>
        <h3> Node を編集し、木の Root Node を更新する </h3>
        <p> updateRootNodeやupdateRootNodeWithで更新できる </p>
        <p> 内部でSTMを使っているのでIOモナドを返す</p>
<pre>
updateRootNode :: Jungle -&gt; String -&gt; Node -&gt; IO ()
updateRootNodeWith :: (Node -&gt; Node) -&gt; Jungle -&gt; String -&gt; IO ()
</pre>
        <p> updateRootNodeには編集したNodeを渡すことで木の Root Node を更新できる </p>
        <p> updateRootNodeWithにはNodeを編集する関数 (Node -&gt; Node) を渡すことで木の Root Node を更新できる </p>
</article>

<article>
				<h3> 性能計測 </h3>
        <p> Jungle がマルチコアプロセッサで性能が出るのか、実用的なWebサービスが提供できるのか確認する </p>
        <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 class="smaller">
				<h3> Haskell コンパイラ </h3>
        <p> Haskell のコンパイラには The Glasgow Haskell Compiler (GHC) を利用する </p>
        <p> 現在の GHC の安定版 7.6.3 は並列時にIOマネージャーがスケールしないという問題がある </p>
        <p> リリース候補版である 7.8 を用いることにより、よりよい性能を得ることができた </p>
        <div align="center">
          <img src="images/ghc.png" width="600px">
        </div>
</article>

<article>
				<h3> 親和性機能(affinity)を利用する </h3>
        <p> 親和性機能とは OS スレッドをCPUコアに固定する機能 </p>
        <p> 並列実行時の性能が向上するため性能計測で利用する </p>
        <p> Haskell は実行時に -qa オプションを使うこと親和性機能を使うように指示できる </p>
</article>

<article class="smaller">
				<h3> 読み込みの性能計測 </h3>
        <p> 木構造の読み込みにかかる時間を計測する </p>
        <p> 12 スレッドで親和性機能を使って実行した場合 10.37 倍の性能向上<br>
            →実コア数である12コアまで、ほぼ線形にスケールする</p>
        <p> 親和性機能を使った時 24スレッドで実行すると実行時間が大幅に伸びる </p>
        <div align="center">
          <img src="images/read.png" width="600px">
        </div>
			</article>

<article class="smaller">
				<h3> 書き込みの性能計測 </h3>
        <p> 木構造の書き込みにかかる時間を計測する </p>
        <p> 12 スレッドで親和性機能を使って実行した場合 3.82 倍の性能向上<br>
            → 同時に書き込みがあった場合、STMが処理をやり直すため並列度がでない</p>
        <p> 読み込みと同様に、親和性機能を使って24スレッドで実行すると実行時間が大幅に伸びる </p>
        <div align="center">
          <img src="images/write.png" width="600px">
        </div>
</article>

<article>
				<h3> 読み込みと書き込みの考察 </h3>
        <p> 読み込みが高速 </p>
        <p> 書き込みは、同時に書き込まれた場合 STMが処理のやり直しをするため、並列度がでない </p>
        <p> 親和性機能を使った場合 24 スレッドでは遅くなる<br>
            ⇒ 全てのCPUコアにスレッドを固定することになりスケジューリングがうまくいかないためだと考えられる
        <p> 書き込みより読み込みが多用されるシステムに向いている </p>
</article>

<article>
				<h3> Webサービスに組み込んでの性能計測 </h3>
        <p> Haskell の HTTP サーバ Warp と組み合わせて Web掲示板サービスを開発する </p>
        <p> 測定ツール weighttp を用いて掲示板に読み込みと書き込みで負荷をかける。 </p>
        <p> Warp は、ハイパースレッディングで明らかに遅くなるので使わない </p>
</article>

<article>
				<h3> ネットワークのボトルネック </h3>
        <p> ネットワークのボトルネックが大きい </p>
        <p> 並列環境でどのようにスケールするか計測したいため、ネットワークを介さずに性能計測を行う </p>
        <br>
        <div align="center">
          <img src="images/request.png" width="300px">
        </div>
</article>


<article class="smaller">
				<h3> Webサービスに組み込んでの性能計測 </h3>
        <p> 読み込みと書き込みの実験結果 </p>
        <p> 1秒間あたりどれだけリクエストを捌けるかという指標で比較<br>
            ⇒大きければ大きいほど性能が良い </p>
        <p> 8 スレッドで実行時、読み込みは 6.18 倍、書き込みは3.93倍の性能向上 </p>
        <p> Jungle 単体での実験結果と同じで、読み込みのほうがスケールする </p>
        <div align="center">
          <img src="images/bbs.png" width="600px">
        </div>
</article>

<article>
				<h3> Java との性能比較 </h3>
        <p> HaskellとJavaで同様のWeb掲示板サービスを用意する </p>
        <p> 実環境を想定して、ブレードサーバ2台用意し、ネットワークを介して負荷をかける </p>
        <p> 100 万リクエストを処理するのにかかる時間を計測 </p>

        <table>
          <tr>
            <th>測定
            <th>Haskell
            <th>Java
          </tr>
          <tr>
            <td>読み込み
            <td>16.31 s
            <td>53.13 s
          </tr>
          <tr>
            <td>書き込み
            <td>20.17 s
            <td>76.4 s
          </tr>
        </table>

        <p> 読み込みで 3.25 倍、書き込みで 3.78 倍の性能差<br> 
            ⇒ Haskell は実用的なWebサービスを開発できる </p>
</article>

<article>
				<h3> Java との生産性の比較</h3>
        <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> Haskell の並列処理は、最新のコンパイラで性能がでることがわかった </p>
        <p> 読み込みに関して 12 コアで実行した場合、10.37 倍 という性能向上率が確認できた </p>
        <p> また、Web 掲示板サービスを開発し、Java と比較して読み込みで 3.25 倍、書き込みで 3.78 倍の性能が確認できた </p>
        <p> Network 越しの計測では並列性能があまり出ていないことがわかった </p>
</article>

<article>
				<h3> 今後の課題 </h3>
        <p> ネットワークボトルネックの改善 </p>
        <p> 分散データベースとしての実装 </p>
        <p> 永続性の実装 </p>
</article>

<article>
				<h3>ネットワークボトルネックの改善</h3>
        <p>ネットワークを経由した際に性能が向上しない</p>
        <p>ネットワークの負荷の掛け方などボトルネックを回避する方法を考える</p>
</article>

<article>
				<h3>分散データベースとしての実装</h3>
        <p>Java の Alice に相当する以下の機能を実装する必要がある</p>
        <ul> 
          <li>サーバ間でデータをやり取りする仕組みの実装
          <li>トポロジーの形成機能の実装
          <li>木構造の変更を共有するためのマージアルゴリズムの実装
        </ul>
</article>

<article>
				<h3>永続性の実装</h3>
        <p>現在、オンメモリ上で動作するデータベース</p>
        <p>並列性を損なわない形で、ディスクへの書き出しを実現したい</p>
        <p>書き出しを担当するスレッドを用意するといったことが考えられる</p>
</article>

<article>
				<h3>
          ネットワークボトルネックの計測
				</h3>
        <p>
        ネットワークを介す場合と介さない場合のWarpのベンチマーク
        </p>
        <div align="center">
          <img src="images/warp.png" width="600px">
        </div>
</article>


	</body>
</html>