Realm vs ObjectBox vs SQLite パフォーマンス比較

#はじめに
1ファイルで管理できる軽量なDBを探している。
候補は今のところRealm,ObjectBox,SQLite。
今回はそれらのパフォーマンスを比較してみた。

#実験

Realm,ObjectBox,SQLiteでそれぞれ実験用のアプリを作成し、SELECTに要する時間などを計測した。INSERTについては今回は比較してない。
試験にはXperia XZs (Android8.0)を使用し、デバッガによる実行ではなく、直接起動して実験した。
試験コードは以下ののGitHubリポジトリに置いている。
https://github.com/wakino/android-database-performance

###実験用データ
以下が実験に使ったテーブル（ObjectBoxの例）になる。
このテーブルのレコードを1万件分INSERTしたデータベースを用意して実験した。

```java
public class TestData extends RealmObject {
	@PrimaryKey
	private long id;
	private int normalNumber;
	@Index
	private int indexedNumber;
	private String normalText;
	@Index
	private String indexedText;
}
```
###ファイルオープン
DBファイルのオープンに要する時間を計測した。DBそのものの初期化についてはこの時間には含まれない。

|             |Realm  | ObjectBox | SQLite |
|:-----------:|:-----:|:---------:|:------:|
|オープン時間 |22.3   | 30.7      |7.6     |
※単位はmsec


###1レコードのSELECT（Index無し)
先頭、5000番目、末尾のレコードを条件指定して１個だけ取得した場合の時間を計測した。
TestDataのnormalNumberとnormalTextがクエリ対象になる。
それぞれ１万回試行して、その平均を記録した。


|データ      |Realm | ObjectBox | SQLite |
|:----------:|:----:|:---------:|:------:|
| 数値#1     |0.2   |0.2        |1.9     |
| 数値#5K    |0.2   |1.5        |2.1     |
| 数値#10K   |0.2   |2.7        |2.1     |
| 文字列#1   |0.2   |0.2        |2.1     |
| 文字列#5K  |0.3   |1.7        |2.1     |
| 文字列#10K |0.4   |2.8        |2.2     |
※単位はmsec

###1レコードのSELECT（Index有り）
先頭、5000番目、末尾のレコードを条件指定して１個だけ取得した場合の時間を計測した。
TestDataのindexedNumberとindexedTextがクエリ対象になる。
試験は１万回行い、それの平均を記録した。

|データ      |Realm | ObjectBox | SQLite |
|:----------:|:----:|:---------:|:------:|
| 数値#1     |0.1   |0.2        |0.5     |
| 数値#5K    |0.2   |0.2        |0.4     |
| 数値#10K   |0.2   |0.2        |0.4     |
| 文字列#1   |0.2   |0.2        |0.5     |
| 文字列#5K  |0.2   |1.6        |0.4     |
| 文字列#10K |0.2   |2.8        |0.4     |
※単位はmsec

###100レコードのSELECT
Index無しのデータをキーにして末尾から100件分をクエリしてそのデータを使って簡単な演算を行った時間を計測した。

|データ      |Realm | ObjectBox | SQLite |
|:----------:|:----:|:---------:|:------:|
| 取得時間   |3.2   |4.6        |3.3     |
※単位はmsec


###ファイルサイズ
|データ      |Realm | ObjectBox | SQLite |
|:----------:|:----:|:---------:|:------:|
| 取得時間   |1048  |2650       |876     |
※単位はKB


#考察

###ファイルオープン
SQLite > Realm > ObjectBoxの順で、SQLiteの圧勝。

###1レコードのSELECT（Index無し)
Realm > ObjectBox > SQLiteでRealmの圧勝
Realmは早い上に末尾のレコードでも検索時間があまり大きくならないのが素晴らしい。
ObjectBoxは末尾に行くに従って時間がかかるようになる。
SQLiteはどの位置でも同じような検索時間だが、どのようなデータ構造なのだろうか？

###1レコードのSELECT（Index有り）
Realmはインディクス無しでも十分早いので、インディクスをつけてもそこまで差が無いが、検索位置にかかわらず一定の検索時間になった。
ObjectBoxは文字列についてはインディクスの効果が無いように見える。実装をチェックしたけど問題は見つけられず謎のまま。<font color="Red">（試験に使ったVersionに問題がありそうとのご指摘を頂いたが、まだ最新版では未評価）</font>
SQLiteはインディクスの効果は絶大だがRealmには及ばない。



###100レコードのSELECT
ObjectBoxはちょっと遅い。RealmとSQLiteは同等に見える。

###ファイルサイズ
大きさはObjectBox > Realm > SQLiteの順。SQLiteが一番小さい。ObjectBoxはSQLiteの3倍近くになる。
実は最初の実験ではRealmのファイルサイズが9MB近くになって圧倒的に大きかったのだが、調査してみるとINSERT時のトランザクションのかけ方によってサイズが変わることが分かった。
１万レコードを１回のトランザクションで一気に書き込むと１万件のトランザクションログも一緒に保存されてしまうようだ。なので今回の実験では100個単位でトランザクションをかけるようにした。
詳細はStackOverflowの以下の記事を参照。

https://stackoverflow.com/questions/46228149/unexpectedly-large-realm-file-size


#まとめ
今回の実験だけで評価するならRealm > ObjectBox >> SQLiteという感じだが、それぞれ長所・短所があるので、ユースケースによっては当然この順位も変わってくる。それと付け加えるならRealmは初回クエリに結構時間がかかっている。おそらくキャッシュしているのだろうが、当然このキャッシュサイズによってパフォーマンスは変わるわけで、メモリ使用量という観点だと、ObjectBoxのほうが良いのかもしれない（調べてはない）。
今回はINSERTの評価を行わなかったが、以下の記事によるとObjectBoxはINSERT,UPDATE性能はかなり良いようだ。
https://notes.devlabs.bg/realm-objectbox-or-room-which-one-is-for-you-3a552234fd6e
使い勝手についてはRealmとObjectBoxは大差無いように思う。SQLiteはRoomなどと組み合わせれば使い勝手は良くなるが、それでもまだ面倒さは残る。