Haskellによる非破壊的木構造を用いたCMSの実装

Daichi TOMA
Sep 13, 2013

Haskellによるデータベースの実装

ウェブサービスの利用者の増加の予想は困難である。 ウェブサービスは、負荷が増大した際に容易に拡張できるスケーラビリティがあることが望ましい。

ウェブサービスのスケーラビリティの実現のネックは、データベースである。 本研究室では、スケーラビリティのあるデータベースとして Jungle を開発している。 Jungle には、既に Java による実装があり、分散データベース Cassandra 以上の性能が確認できるなどの成果がでている。

本研究では、Haskell による Jungle の再実装を行った。 その結果、Java 版と同程度の性能を得ることができた。また、Java 版の実装と比較し、開発期間及びコード行数の短縮ができた。

非破壊的木構造データベース Jungle

Jungle は、非破壊的木構造を取り扱うデータベースである。 非破壊的木構造は、元の木構造を書き換えることなく、編集を行うことが可能である。

非破壊的木構造の特徴

非破壊的木構造は以下の特徴を持つ。

ロックを必要とせず、また破壊されることがないため、自由にコピーを作成することができる。 コピーを複数作成することで、アクセスを分散させることが可能であり、スケーラビリティが確保できる。

この非破壊的木構造を取り扱うデータベースが Jungle である。

非破壊的木構造データベース Jungle

Jungle は複数の木を保持することができる。 それぞれの木には名前がついており、名前を用いることで最新のルートノードへアクセスすることができる。

木のノードには、属性と属性値の組がある。 また、順序のある子ノードを持つ。

ノードの属性と属性値、子ノードは編集可能である。 編集には、ルートノードと編集するノードへのパスによって行われる。 編集すると新しい木が作成される。

最新版は、サーバ毎に異なる可能性がある。 木へのアクセスは並行して行われ、競合したアクセスはマージにより解決される

本研究では、Haskell による Jungle の再実装を行った。

Haskell で実装する利点

非破壊的木構造は、破壊的な代入のない Haskell と相性がよいと考えられる。 Haskell は、破壊的な代入許さず、参照透過性を持つ。

参照透過性を持つため、ソースコードは明瞭となる。 また、どのようなタイミングで評価しても結果は同じとなるため、並行に評価することができる。

Haskell は定理証明支援系との相性がよく、信頼性の高いシステムソフトウェアができる可能性がある

コード行数及び開発期間の短縮

Haskell では、再帰的なデータ構造を定義することが可能で、木構造がデータ型として定義できる。 また、パターンマッチにより、シンプルに場合分けをすることが可能である。 そのため、コード行数を短くすることが可能で、開発期間の短縮にも繋がった。

Java 版の Jungle の実装と比較すると、コード行数は約 3000 行から約 150 行へ短くなった。 また開発期間は Java 版の実装で、3 ヶ月程度かかったが、 Haskell 版の実装は 2 週間程度であった。

Haskell 版 Jungle の API

Jungle は、様々なシステムに組み込んで使用できる。 Haskell 版 Jungle の利用方法について述べる。

木の作成

Jungle は複数の木を保持することができる。 木には名前がついており、名前を利用して、木構造の取得や削除を行うことができる。

jungle = createJungle
new_jung = createTree jungle "new_tree"

createTree 関数を利用して、"new_tree"という名前の木構造を作成するコードである。

ルートノードの取得

ルートノードを取得するためには以下のように記述する。

tree = getTreeByName new_jung "new_tree"
node = getRootNode tree

getTreeByName 関数で名前を指定することで木構造を取得できる。 getRootNode 関数でルートノードを取得できる。

子の追加及び属性の追加

addNewChildAt 関数で、ノードに新しい子を追加することができる。 また、putAttribute 関数で、ノードに属性を追加できる。

new_tree = addNewChildAt tree [0,1] 0
new_tree2 = putAttribute new_tree [0,1,0] "key" "value"

どのノードを編集するかという情報は、ルートノードからのパスを渡すことで解決する。

パス

パスは Haskell のリストとして定義する。 ルートノードからスタートし、ノードの子供の場所を次々に指定していくことで編集対象のノードの場所を表す。

掲示板システム

これらのAPIを利用して、ウェブサービスを構築できる。 Jungle を用いた CMS として掲示板システムを作成した。

掲示板システムの HTTP サーバには、Warp を用いる。 Warp は、Haskell で書かれた軽量・高速な HTTP サーバでプログラムに組み込んで利用することが可能である。

ベンチマーク

掲示板システムを利用して、Haskell 版 Jungle と Java 版 Jungle の性能比較を行う。

Java 版 Jungle には、HTTP サーバ Jetty を用いた掲示板システムの実装があり、それを採用する。

実験方法を以下に示す。

実験環境

負荷をかける対象であるサーバは、マルチコア環境が生かされているか確認するためにコア数の多いマシンを用いる

性能評価に使用するサーバ

OS Fedora 16
CPU Intel Xeon X5650 2.67GHz * 2
論理コア数 24
物理メモリ 132 GB

実験結果


左が読み込み、右が書き込みの実験結果である。 縦軸は実行時間、横軸はノード数である。

ノード数が増えると負荷が上昇するため、実行にかかる時間が伸びている。

Haskell 版および Java 版の Jungle は、ほぼ同程度の速度が出ていることが分かる。

Haskell 版 Jungle の課題

Haskell版 Jungle は、Java 版と同程度の速度が出ているが、まだ速度向上の余地がある。

並列実行と、メモリ領域効率の改良が可能である。

並列実行

Haskell 版 Jungle では、並列実行に改良の余地がある。 現在、Haskell 版 Jungle はシングルコアで実行している。

並列実行を行った場合、複数のスレッドが立ち上がり、処理を行っていることは確認できた。 しかしながら、シングルコアで実行した場合と比較して実行結果が遅くなる。 マルチコアで実行した際の速度向上の達成のために、オーバーヘッドとなっている部分を見直し改善する必要がある。

メモリ領域の効率

Haskell 版 Jungle は、全ての評価を遅延評価で行っている。 書き込みの際、何かしらの結果を表示するまで、簡約可能な式の状態で積まれたままとなる。

適切な箇所で、即時評価を行うように変更することでメモリ領域の効率を改善できる。

メモリ領域の効率は、大量に書き込みだけを行った場合に問題となる。 大量に書き込みだけを行った場合、簡約可能な式が積まれ続ける。 その際、効率のよい領域に入りきらないサイズになると実行結果が遅くなる。 現在は、推奨されるヒープ領域のサイズを変更している。

メモリ領域の効率

ヒープ領域のサイズを変更しない場合の実験結果を示す。 また、読み込みを行った際に、実行速度が改善することを示すために、クラスタ台数を10台増やすごとに一度読み込みを挟む。

書き込みを繰り返すと実行時間が悪化し、読み込み後、実行時間が下がる。 読み込みの際には、数万回以上の書き込みを処理するため数秒から数十秒かかる。 書き込みの順序は正しく処理されている。

まとめ

今後の課題