view slides/master.html @ 70:0b1a059c49fa

fix
author kono
date Thu, 13 Feb 2014 12:08:24 +0900
parents 5e39ab004ae0
children 98a55a935619
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 は rseq により並列実行を指示し、STMにより共有データを実現する</p>
        <p> Haskellの並列処理は、最新のコンパイラで性能がでることがわかった</p>
        <p> 読み込みに関して 12 コアで実行した場合、10.37 倍 という性能向上率が確認できた </p>
        <p> Web 掲示板サービスを開発し、Java と比較して読み込みで 3.25 倍、書き込みで 3.78 倍の性能差が確認できた </p>
        <p> Network および IO の並列性能はあまり出てないことがわかった</p>
</article>

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

<article>
	<h3> Haskellの型検査の例(list) </h3>
      :
     / \
    1   []
<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>

          Haskell の並列処理
rparの例
			<article>
				<h3>
          Haskell の遅延評価
				</h3>
        <p>
        並列実行は遅延評価が問題になる
        </p>
        <p>
        haskell では値が必要となるまで式の評価が行われない
        </p>
        <p>
        並列に実行するように指示しても、評価が行われなければ並列に動かない
        </p>
			</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>
        <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 は、ノンブロッキングで共有データを扱える </p>
        <p> 共有データの変更は以下の様に行われる </p>
        <ul>
          <li>他から変更がなければそのまま適用
          <li>変更している間に、他から変更されれば変更処理をやり直し
        </ul>
</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>
          木から最新のルートノードを取ってくる
				</h3>
        <p>
        木から最新のルートノードを取ってくるにはgetRootNodeという関数を使う
        </p>
<pre>
getRootNode :: Jungle -&gt; String -&gt; IO Node
</pre>
        <p>
        Jungleと木の名前を渡すと最新のルートノードが返ってくる
        </p>
			</article>

<article>
        <h3> ノード </h3>
        <p> Node はChildren(Node)とattributesを持つ </p>
        <p> Jungle の参照や変更の関数は全てノードに対して行う </p>
        <div align="center">
          <img src="images/node_component.png" width="500px">
        </div>
</article>

<article>
	<h3> ノードを編集し、木の最新のルートノードを更新する </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を渡すことで木の最新のルートノードを更新できる </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>
          親和性機能(affinity)を利用する
				</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スレッドで実行すると実行時間が大幅に伸びる。
        スレッドが CPU に固定されるため、性能計測以外のプログラムがうまくスケジューリングされないためだと考えられる
        </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>
        <p>
        読み込みと同様に親和性機能を使って24スレッドで実行すると実行時間が大幅に伸びる
        </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>

			<article>
				<h3>
          GHC 7.6.3 の IO マネージャーの問題
				</h3>
        <p>
        GHC 7.6.3 には 並列実行時に IO マネージャーがスケールしないという問題がある
        </p>
        <div align="center">
          <img src="images/ghc.png" width="700px">
        </div>
			</article>

	</body>
</html>