JavaScript
indexedDB

indexedDBベンチマーク

More than 5 years have passed since last update.


indexedDBベンチマーク

みなさんindexedDB使ってますか。

あんまり名前は聞きませんけど、トランザクションが張れて処理をロールバック出来たり、インデックスである程度の検索が出来たり、結構頼りになるやつなんですよ。

今回はそんなindexedDBくんのベンチマークを簡単に測ってみることにしました。


方法

次のプロパティを持つオブジェクトをオブジェクトストアに突っ込んでいきます。


  • id ( auto increment )

  • group

  • flag

突っ込むオブジェクトストアにはKeyPathとしてauto incrementな"id"プロパティを定義します。(MySQLとかでいうPRIMARY KEY AUTOINCREMENTみたいなものですね)

あと読み込みテストの際にインデックスを使いたいので、"group", "flag"プロパティを対象にインデックスを1つ作成します。

各プロパティの値は、"group"プロパティは0~19、"flag"プロパティは0~1の範囲でランダムな値を設定します。

書き込み時間のチェックとして、上記のオブジェクト10000件,50000件,100000件をオブジェクトストアに突っ込むのにかかる時間を測定します。

そのあと読み取り時間のチェックとして、10000件,50000件,100000件を突っ込んだ場合それぞれに対して、"group"プロパティの持つ値別に"flag"プロパティが1であるオブジェクトがいくつあるかを測定します。

オブジェクト自体の読み込み時間等も測定したいですが、とりあえずcountだけ見れれば個人的に満足なのでopenCursorでカーソル開いてごにょごにょとかはしません。


使用コード

このまままるっとコピーして.htmlとして保存すれば誰でも走らせることが出来ます。

indexedDBは便利なんですが、やたらとコールバックまみれになるのがちょっとアレですね


index.html

<!DOCTYPE html>

<html>
<head>
<meta charset="utf-8" />
<script type="text/javascript">

MAX_LOOP_COUNT = 5;
PUT_OBJECT_COUNT = 10000;
GROUP_COUNT = 20;

( function( maxCount ) {

start( 1 );

function start( currentCount ) {

// DBはあらかじめ削除
indexedDB.deleteDatabase( 'benchmark' ).onsuccess = function( ) {

var req = indexedDB.open( 'benchmark', 1 );
req.onsuccess = function( e ) {
// 書き込みテストからスタート
writeBenchmark( e.target.result, currentCount );
};

// オブジェクトストアとインデックスを作成
req.onupgradeneeded = function( e ) {
var db = e.target.result;
db.createObjectStore( 'record', {
keyPath: 'id',
autoIncrement: true
} )
.createIndex(
'idx_benchmark',
[ 'group', 'flag' ]
);
};

};
}

function writeBenchmark( db, currentCount ) {

// ランダムなデータをPUT_OBJECT_COUNT件だけ書き込む
console.time( 'write benchmark' + currentCount );
var tx = db.transaction( [ 'record' ], 'readwrite' );
var record = tx.objectStore( 'record' );
for( var i = 0; i < PUT_OBJECT_COUNT; i++ ) {
record.put( {
group : Math.floor( Math.random( ) * GROUP_COUNT ),
flag : Math.floor( Math.random( ) * 2 )
} );
}

tx.oncomplete = function( ) {
console.timeEnd( 'write benchmark' + currentCount );
countBenchmark( db, currentCount ); // 完了したら読み込みテストへ
};

}

function countBenchmark( db, currentCount ) {
console.time( 'read benchmark' + currentCount );
var tx = db.transaction( 'record', 'readonly' );
var idx = tx.objectStore( 'record' ).index( 'idx_benchmark' );

var req_order = [ ];
for( var i = 0; i < GROUP_COUNT; i++ ) {
var flag_count = IDBKeyRange.only( [ i, 1 ] ); // 特定のgroupでflagが1のものを検索
var req = idx.count( flag_count );

req_order.push( i );
req.onsuccess = function( e ) {
// 本当にカウントできているかを確認する場合はコメントアウトを外す
//console.log( 'Group' + req_order.shift( ) + ' counted: ' + e.target.result );
};
}

tx.oncomplete = function( ) {
console.timeEnd( 'read benchmark' + currentCount );
db.close( );
if( currentCount++ < maxCount ) {
start( currentCount );
}
};
}
} )( MAX_LOOP_COUNT );

</script>
</head>
<body>
</body>
</html>



実測

上記の.htmlをChrome(32.0.1700.76 m)に放り込んでコンソールを開くと次のような感じになります。


write benchmark1: 530.000ms

read benchmark1: 49.000ms

write benchmark2: 522.000ms

read benchmark2: 44.000ms

write benchmark3: 539.000ms

read benchmark3: 47.000ms

write benchmark4: 529.000ms

read benchmark4: 47.000ms

write benchmark5: 526.000ms

read benchmark5: 45.000ms


いいですね。書き込みはともかく、読み取りに関しては体感できないくらいでしょうか。

しかしこれは10000件です。50000, 100000件でも試してみましょう。この場合はコード中の変数PUT_OBJECT_COUNTを適当に変えればOKですね。

あと折角なので他のブラウザでも試します。

FirefoxとOperaが手頃だったので、とりあえずそれで速度を測定しちゃいましょう。IEはfile://スキーマじゃindexedDBは使わせないだの、console.timeがないだの言われたので面倒になったので測ってません。バージョン11だと使えるのかもしれませんが、あいにく手元のIEはバージョン10でした。

というわけで測定した結果は以下の通りです。

コードにある通り、それぞれの書き込み件数の場合を5回ずつ測定し、以下のテーブルにはその平均を記載しています。値の単位はミリ秒(ms)です。

ブラウザ
バージョン
10000件書き込み
10000件カウント
50000件書き込み
50000件カウント
100000件書き込み
100000件カウント

Chrome
32.0.1700.102 m
529.4
48.8
3028.2
224.4
6524.6
464.0

Opera
19.0
454.6
40.4
2597.4
181.4
5595.0
376.6

Firefox
26.0
766.1
3.0
3853.8
7.2
7897.4
12.2

Firefoxすごい。他と比べると書き込み速度は見劣りしますが、読み取り速度が段違いです。内部でうまい具合にキャッシュを持ったりしているのかもしれません。

しかし他のブラウザも、100000件のテストで処理時間1秒未満と個人的には十分満足な速度です。

実際に使用する場合はもう少しサイズの大きいオブジェクトを突っ込むことになると思うんですが、その場合は容量の関係でオブジェクトの総数は少なくなりますし、速度面に関してはそこまで気にせずコードを書いても違和感のないものになりそうですね。