2007年10月17日

SQLite性能評価その4

SQLite性能評価その3」の続きです。 今回はインメモリデータベースの性能を評価してみました。

インメモリデータベースで100000件追加処理色々

測定環境は前回と同じです。

コードは、前回のCase2~Case5の

sqlite3_open("testdb.sq3", &Db);

を、

sqlite3_open(":memory:", &Db);

に置き換えるだけです。
それぞれCase2(Memory)~Case5(Memory)と呼ぶことにします。

測定結果

測定結果は以下のようになりました。 データベースのopen、closeや、CREATE TABLEの時間は含めていません。
処理時間

ファイルを使った前回の測定結果とも比べてみます。
処理時間

Case4とCase5のsqlite3_bind_xxxを使ったデータ追加はインメモリデータベースでも効果が高いようです。

SQLiteの性能評価その5」に続きます。

2007年10月08日

SQLite性能評価その3

SQLite性能評価その2」の続きです。 テーブルに100000件追加する処理だけでも、Case2の他にいろいろな書き方があるので、試してみました。

100000件追加処理色々

sqlite3_execの代わりにsqlite3_prepareとsqlite3_stepを使ってみました。これをCase3とします。

// Case3
struct sqlite3* Db;
struct sqlite3_stmt* Statement;
char Buffer[256];

sqlite3_open("testdb.sq3", &Db);
sqlite3_prepare(Db, "CREATE TABLE test1(name TEXT,value INTEGER)", -1, &Statement, NULL);
while (sqlite3_step(Statement) == SQLITE_BUSY);
sqlite3_finalize(Statement);
// 処理時間測定開始
sqlite3_prepare(Db, "BEGIN", -1, &Statement, NULL);
while (sqlite3_step(Statement) == SQLITE_BUSY);
sqlite3_finalize(Statement);
for (int i = 0; i < 100000; ++i) {
  char* Sql = sqlite3_snprintf(sizeof(Buffer), Buffer, "INSERT INTO test1 VALUES('%q',%d)", StringList[i].c_str(), ValueList[i]);
  sqlite3_prepare(Db, Sql, -1, &Statement, NULL);
  while (sqlite3_step(Statement) == SQLITE_BUSY);
  sqlite3_finalize(Statement);
}
sqlite3_prepare(Db, "COMMIT", -1, &Statement, NULL);
while (sqlite3_step(Statement) == SQLITE_BUSY);
sqlite3_finalize(Statement);
// 処理時間測定終了
sqlite3_close(Db);

sqlite3_prepare の真価」で紹介されている方法を使ってみました。 これをCase4とします。

// Case4
struct sqlite3* Db;
struct sqlite3_stmt* Statement;

sqlite3_open("testdb.sq3", &Db);
sqlite3_prepare(Db, "CREATE TABLE test1(name TEXT,value INTEGER)", -1, &Statement, NULL);
while (sqlite3_step(Statement) == SQLITE_BUSY);
sqlite3_finalize(Statement);
// 処理時間測定開始
sqlite3_prepare(Db, "BEGIN", -1, &Statement, NULL);
while (sqlite3_step(Statement) == SQLITE_BUSY);
sqlite3_finalize(Statement);
sqlite3_prepare(Db, "INSERT INTO test1 VALUES(?,?)", -1, &Statement, NULL);
for (int i = 0; i < 100000; ++i) {
  sqlite3_reset(Statement);
  sqlite3_bind_text(Statement, 1, StringList[i].c_str(), static_cast<int>(StringList[i].size()), SQLITE_TRANSIENT);
  sqlite3_bind_int(Statement, 2, ValueList[i]);
  while (sqlite3_step(Statement) == SQLITE_BUSY);
}
sqlite3_finalize(Statement);
sqlite3_prepare(Db, "COMMIT", -1, &Statement, NULL);
while (sqlite3_step(Statement) == SQLITE_BUSY);
sqlite3_finalize(Statement);
// 処理時間測定終了
sqlite3_close(Db);

Case4のsqlite3_prepareをsqlite3_prepare_v2に変えてみました。これをCase5とします。

// Case5
struct sqlite3* Db;
struct sqlite3_stmt* Statement;

sqlite3_open("testdb.sq3", &Db);
sqlite3_prepare_v2(Db, "CREATE TABLE test1(name TEXT,value INTEGER)", -1, &Statement, NULL);
while (sqlite3_step(Statement) == SQLITE_BUSY);
sqlite3_finalize(Statement);
// 処理時間測定開始
sqlite3_prepare_v2(Db, "BEGIN", -1, &Statement, NULL);
while (sqlite3_step(Statement) == SQLITE_BUSY);
sqlite3_finalize(Statement);
sqlite3_prepare_v2(Db, "INSERT INTO test1 VALUES(?,?)", -1, &Statement, NULL);
for (int i = 0; i < 100000; ++i) {
  sqlite3_reset(Statement);
  sqlite3_bind_text(Statement, 1, StringList[i].c_str(), static_cast<int>(StringList[i].size()), SQLITE_TRANSIENT);
  sqlite3_bind_int(Statement, 2, ValueList[i]);
  while (sqlite3_step(Statement) == SQLITE_BUSY);
}
sqlite3_finalize(Statement);
sqlite3_prepare_v2(Db, "COMMIT", -1, &Statement, NULL);
while (sqlite3_step(Statement) == SQLITE_BUSY);
sqlite3_finalize(Statement);
// 処理時間測定終了
sqlite3_close(Db);

測定結果

測定結果は以下のようになりました。
処理時間

分かったこと。

  • Case2とCase3のほとんど同じです。sqlite3_execで済むときはこれを使えば良いでしょう。
  • Case3とCase4はだいぶ違います。処理時間が6割近く削減できています。複数のデータを追加するときは、Case4の方式を使いましょう。
  • Case4とCase5はほとんど同じです。sqlite3_prepare_v2を使うことによる速度的なメリットは無いようです。

SQLiteの性能評価その4」に続きます。

2007年10月07日

WTL紹介

Windowsで(Pure)C++とWin32でGUIアプリを作るとき、GUIライブラリがあると楽です。
私は昔はGUIライブラリとしてMFCを使っていました。 しかし、MFCは、Visual C++の各バージョンに依存していて、Visual C++のバージョンを新しくするだけで問題が起こることもありました。これはちょっと勘弁して欲しかったので、もうちょっとポータビリティが高いWTLというライブラリを使うことにしました。

WTLの公式サイトによると、 2007/6/10にWTL 8.0がリリースされています。古くて枯れているというライブラリではありません。結構新しいです。

日本語化パックも配布されています。
日本語パックをインストールすると、アプリケーションウィザードと最初に生成されるリソースファイルなどが日本語化されます。

Visual C++ 2005 Expressでも使えるそうです。
Expressは、MFCが使えないので、GUIライブラリとしてはWTLが最有力候補でしょう。

しかし、WTLはドキュメントが少ないため、慣れるまではあまり楽ではありません。
私は「ATL/WTL」を一番参考にしていますが、 ここもリファレンスがあるわけではないので、細かい使い方は手探りです。
MFCと関数名が似ているので、MFCのマニュアルも結構参考になりますが、 どうしても分からないときは、WIN32API名を元にWTLのコードをgrep検索して見つけています。

こんな使い方になってしまうので、MFCやWIN32APIを多少知っていないと使いこなせないライブラリです。 使うときはそれなりの覚悟が必要です。
ちゃんとドキュメントが揃っていれば結構良いライブラリだと思うんですけどね。

2007年10月05日

SQLite性能評価その2

SQLite性能評価その1」の続きです。
SQLiteは、ファイル名を":memory:"とすると、インメモリデータベースを構築します。
このとき、前回のCase1とCase2の処理時間を測ってみました。

トランザクション明示的指定の有無による処理時間の変化(インメモリ版)

測定環境は前回と同じです。

コードは、前回のCase1とCase2の以下の部分を、

sqlite3_open("testdb.sq3", &Db);

以下のように置き換えるだけです。

sqlite3_open(":memory:", &Db);

それぞれCase1(Memory)、Case2(Memory)と呼ぶことにします。

測定結果

測定結果は以下のようになりました。 データベースのopen、closeや、CREATE TABLEの時間は含めていません。
処理時間
ほとんど差はありません。インメモリデータベースの場合は、トランザクション処理を明示的に指定しなくても問題はないようです。

Case2だけ前回との違いも比べてみました。
処理時間
25%減です。 ファイルアクセスがなくなったのに思ったより速くなりません。
というより、ファイルアクセスありでも速い、と言うべきなんでしょう。

SQLiteの性能評価その3」に続きます。

2007年10月04日

SQLite性能評価その1

SQLiteの性能を少し見てみたいと思い、 テーブルに100000件のデータを追加した時の処理時間を測ってみました。
テーブルには以下のように文字列と整数の2列を用意しました。

CREATE TABLE test1(name TEXT,value INTEGER)

nameの方は、ランダムなアルファベットで構成される20文字の文字列で、valueの方も乱数としました。

測定環境

測定環境は以下の通りです。

コンパイラVisual C++.NET 2003デフォルトコンパイラ
OSWindows XP Professional SP2
CPUAMD Athlon 64 3700+
メモリ2GB
HDDSeagate ST3300622AS
プロジェクト設定デフォルトRelease構成
SQLiteバージョン3.4.2 (not define THREADSAFE)

SQLiteは、「SQLiteの使い方(Visual C++.NET 2003)」で 紹介した方法で静的ライブラリにして、アプリケーションからC Interfaceで呼び出すようにしました。

トランザクション明示的指定の有無による処理時間の変化

SQLiteは、大量のINSERTを実行する場合は、最初にトランザクションを明示的に開始しておかないとパフォーマンスがとても悪くなるというデータベースです。 (「生まれ変わるPHP - Zend Engine 2、SQLiteの実力は?」に参考情報があります)
まずこれを確認してみました。

トランザクションを開始しない方をCase1とします。 コードは以下のようになります。

// Case1
struct sqlite3* Db;
char Buffer[256];

sqlite3_open("testdb.sq3", &Db);
sqlite3_exec(Db, "CREATE TABLE test1(name TEXT,value INTEGER)", NULL, NULL, NULL);
// 処理時間測定開始
for (int i = 0; i < 100000; ++i) {
  char* Sql = sqlite3_snprintf(sizeof(Buffer), Buffer, "INSERT INTO test1 VALUES('%q',%d)", StringList[i].c_str(), ValueList[i]);
  sqlite3_exec(Db, Sql, NULL, NULL, NULL);
}
// 処理時間測定終了
sqlite3_close(Db);

トランザクションを明示的に開始する方をCase2とします。 コードは以下のようになります。

// Case2
struct sqlite3* Db;
char Buffer[256];

sqlite3_open("testdb.sq3", &Db);
sqlite3_exec(Db, "CREATE TABLE test1(name TEXT,value INTEGER)", NULL, NULL, NULL);
// 処理時間測定開始
sqlite3_exec(Db, "BEGIN", NULL, NULL, NULL);
for (int i = 0; i < 100000; ++i) {
  char* Sql = sqlite3_snprintf(sizeof(Buffer), Buffer, "INSERT INTO test1 VALUES('%q',%d)", StringList[i].c_str(), ValueList[i]);
  sqlite3_exec(Db, Sql, NULL, NULL, NULL);
}
sqlite3_exec(Db, "COMMIT", NULL, NULL, NULL);
// 処理時間測定終了
sqlite3_close(Db);

測定結果

測定結果は以下のようになりました。 データベースのopen、closeや、CREATE TABLEの時間は含めていません。
処理時間
Case2が見えなくなるくらい違いがあります。Case1の処理時間はCase2の約1500倍。ここまでとは・・・。

SQLiteの性能評価その2」に続きます。

2007年10月01日

SQLiteの使い方(Visual C++.NET 2003)

最近、SQLiteというデータベースライブラリの存在を知りました。
シンプルな機能なので扱いやすいデータベースです。中小規模のアプリケーション向けですね。 ライセンスもパブリックドメインなので言うことなし。
スタンドアロンなアプリケーション向けのデータベースとしては、たぶんこれが最有力候補でしょう。

C言語によるソースコードが提供されていますので、C/C++アプリケーションであれば、 アプリケーションに埋め込んで使うことができます。
本日は、アプリケーションに埋め込んで使うための静的ライブラリの作り方を紹介します。

必要なもの

  • Visual C++ 2003.NET
  • SQLite 3.4.2 ソースコード(sqlite-source-3_4_2.zip)。SQLite Download Pageからダウンロード可能
  • プロジェクトファイル(20071001_sqlite.lzh)。ダウンロードはこちら

手順

  1. ダウンロードしてきたファイルを展開します。
  2. sqlite.vcprojをsqlite3.hと同じ場所に置きます。
  3. sqlite.vcprojを開いて全構成をビルドします。libフォルダ以下にC++ランタイムに合わせた6種類のlibファイルが作成されます。

たくさん警告が出ますが、エラーが出なければたぶんOKです。
お手軽データベースライフをお楽しみください。