注意! この記事はQiitaにて公開されていた内容をimportしたものです。
これらの内容は場合によっては陳腐化していて役に立たなくなっていたり、有害であったり、現在の著者の主張と異なることがあります。
皆様の判断の上でご利用いただけますと幸いです(度を超してヤバいものは著者に連絡して頂ければ対応します m(_ _)m)


はじめに

GoogleがRoomを出したので。(3年ぶりですね)

主旨説明

本記事は、「AndroidにおけるSQLite O/Rマッパーでどれを使えばよいのか」という問題を数値比較して検討することを目的とした、以下の記事の更新版という位置づけです。 過去に書いた以下の記事をざっくり読まれることをお勧めします。

エントリーリスト

永続化にSQLiteを用いるもの

  • ORMLite
  • greenDAO
  • ActiveAndroid
  • Room (new!)
  • SugarORM
  • DBFlow
  • DBTools
  • Cupboard
  • Slim Repo
  • RushOrm
  • SquiDB
  • Orma
  • QuantumFlux
  • CPOrm
  • Android-ORM (new!)
  • Electra (new!)
  • requery (new!)
  • Freezer (new!)
  • StorIO (new!)
  • SQLiteMagic (new!)
  • ReActiveAndroid (new!)

永続化に独自形式のファイルを用いるもの

  • Realm
  • Paper
  • ObjectBox (new!)

選定基準

sqldelight, sqlbrite は組み合わせるとORMのように使えますが、それぞれ単体ではORMと見做せないと私は判断しています。よって比較検討に加えていません。 ただ、なかなか興味深いライブラリですし、square社のメンテナンスするライブラリであることから、一見の価値はあります。

なおsqlbriteはdeprecated扱いとなっています。

検証方法

  • Simpleクラスを作成
    • フィールドは以下の図を参照
    • ORMによっては一部のJava型をサポートしていないことがあります。この場合は類似の型で代用しました。
  • 【計測1】データベースに10000件のSimpleインスタンスをINSERTする
    • トランザクション有効時(バルクINSERT)および未使用時の2パターン計測する
    • 一部のNoSQLデータベースではトランザクションを張らずにINSERTすることができないが、これは許容する(グラフでは未計測扱い)
  • 【計測2】INSERTしたレコードのboolean値フィールドを参照し、true のものだけを抽出しリストとして得る
    • 件数が相違ないことを確認する
    • SQLで書くと SELECT * FROM Simple WHERE booleanValue = true
  • 上記【計測】の計測を3回行い、平均値を算出する。この平均値をスコアとして評価する。

検証端末には前回と同じく「Xperia A SO-04E」(Android 4.2.2)を使用します。 選定理由は、昨年との比較がしやすいと考えたからです。 (SQLiteバージョンは 3.7 と推察されます。詳しくは: android.database.sqlite  |  Android Developers )

検証用ソースコード

コミット大歓迎です(今回がFINALなのであんまり意味ないけど。。。)

https://github.com/keima/MostPowerfulOrmInAndroid

免責事項

本記事での検証方法では、以下の点で公平さを欠くものと筆者は考えています。 よって、本結果を利用される際は自己責任でお願いします。 「この検証結果によって生じたいかなる問題についても筆者は一切の責任を負いかねます」。 # ただし、より良くしたいとは思っているので、優しく指摘いただけると嬉しいです。

  • 必ずしもテスト実行方法が統一されていない
    • Applicationクラスに依存するもの、そうでないもの・・・
    • テストケース毎の終了処理
    • ベンチマーク結果で差が出ないよう配慮していますが、公平ではない結果を導いている可能性があります
  • トランザクション対応がORMごとに微妙に異なっている都合、不公平なことがあります
    • 独自形式のファイルに書き出すNoSQL ORMについて、Listを受け付けることができるINSERT処理メソッドがある場合(例えば insertAll(items) のようなメソッドが提供されているとして)、それをトランザクション対応していると呼ぶべきかどうか
  • SQLiteバージョンによってスコアが変動する可能性があります
    • もしかするとSQLiteバージョンによってはNoSQL ORMより性能が良い(悪い)ケースもあるかもしれません。またORMの生成するクエリ文によっては、有利・不利があるかもしれません。

結果

https://docs.google.com/spreadsheets/d/14irRYr4V0K2I5I6u4fjo5cwiwRVLEvrUNBsDHEKsaeU/edit?usp=sharing

ランキング

INSERT (Transaction ON) INSERT (Transaction OFF) SELECT
Transaction ON INSERT Transaction OFF INSERT SELECT
Rank INSERT (Transaction ON) INSERT (Transaction OFF) SELECT
1 ObjectBox Room ObjectBox (ObjectBox)
2 SQLiteMagic QuantumFlux SQLiteMagic (greenDAO)
3 greenDAO CPOrm Room (SQLiteMagic)
4 Realm ObjectBox StorIO (Room)
5 Room greenDAO greenDAO (StorIO)

Italic 書式は独自形式のORM)

総評

ベンチマークの意味するところ

トランザクションを行わない状態でのスコアは、ありがちな単一INSERTオペレーションの性能を比較するためのものです。 ただし現実においてはどのORMを使っても差はごく僅かなため、1万回繰り返すことで差を明らかにしています。

トランザクションを有効にした状態のスコアは、単純で大量のINSERTオペレーションの性能を比較するためのものです。 (例えばアプリの初回起動時のデータ取得オペレーション) SQLiteでは大量のINSERT処理において、トランザクションを使うことで高速化が見込まれるため、ほとんどのケースでトランザクションを有効にしたほうがパフォーマンスが良くなります。 (参考資料: Squeezing Performance from SQLite: Insertions – Jason Feinstein – Medium

ORMによってバルクINSERT処理の提供方法は異なっており、SQLiteDatabase#beginTransaction() を公開しているもの、Collectionを受け取るメソッドを提供するもの、また一切提供されないものなど、様々です。

SELECTのスコアはそのままクエリー検索時の応答性を確認しています。 ORMの実装方法によって様々ですが、Cursorをオブジェクトに詰めなおす処理があるもの、完全に別の試みを行っているものもあるため、スコアに特徴が出ます。 ちなみに、トランザクションを使わない状態のINSERTを行ったあとのSELECTで、greenDAOが驚異のスコアを出しているのですが原因はよくわかりません。毎年こうなるので理由はあるんでしょうけれど。

ランキング結果: トランザクションON

Transaction ON INSERT

RushOrmを除くすべてのORMが、バルクINSERT性能でActiveAndroidより速いという結果を示しました(この傾向は本連載ではずっとそうなっていたので、ActiveAndroidが比較的効率の悪い方法をとっているということでしょうか)。

今回の計測では、RoomとRealmがスコアが近い結果となりました(僅差でRealmに軍配)。 しかし、そのRealmより成績の良いORMが3つあります。 greenDAOとSQLiteMagicは今回の計測では僅差でRealmに勝っていますが、ほとんど誤差だと思われます。 SQLiteMagic, greenDAO, Realm, Room, DBFlowはINSERT性能の良いORMだといえそうです。

これらよりも速いObjectBoxは、greenDAOの開発元が新たに開発を進めている独自形式のORMです。 1万件を一気に書き込むような実装になっているのでしょうか。今後どうなっていくのか個人的に興味あります。

ActiveAndroid比でRoomは10倍速いです。ただしトランザクションをちゃんと使った場合です。 トランザクションは適切に使いましょうね。。。

ランキング結果: トランザクションOFF

Transaction OFF INSERT

前回も述べましたが、Realmはトランザクションを適切に使わなかった場合スコアが極端に悪くなります。

ここでついにRoomが首位を獲得します。 QuantumFluxはContentProviderを用いたORMです。これまでQuantumFluxより高速なORMはNoSQLなORMでしたが、更新が止まっていたり、トランザクションONに寄せたりしたため、完全に消え失せてしまっています。

Roomはシングルでの性能がほかのORMよりも高速であるといえそうです。 ActiveAndroid比で3.5倍速いです(もし大量のINSERT処理を書いているなら、トランザクションを使うと10倍速くなる、というのはさっき書きましたね)。 別の言い方をするとRoomは、初心者がトランザクション利用を怠っても、ほかのORMほど悲惨な結果にならないようです。 それでも適切なトランザクション利用時と比べると15倍ほどパフォーマンス悪化するので、トランザクション利用を心がけましょう。

ランキング結果: SELECT

SELECT

(greenDAOが速い理由がよくわからないので、ここではトランザクション利用後のSELECTに絞って記述します)

ここでもObjectBoxが最速です。ただしRoomも3番手につけるくらいには速いです。 RealmはRealmObjectを返すまでは数ミリ秒オーダーで結果を返しますが、その後のRealObjectに戻す処理でボトルネックとなり、今回それほどスコアが伸びませんでした。 これまでRealmが間違いなく速かったのですが、いまはSQLiteなORMでもRealmを超えるスピードを出しており、技術革新は素晴らしきかな、という感想です。

SELECT文においても、ActiveAndroid比でRoomは約7倍速いです。

その他トピック

ActiveAndroidの移行パスとしてのReActiveAndroid

ImangazalievM/ReActiveAndroid: 🚀 Simple and powerful ORM for Android

ActiveAndroidが負債として残ってしまっているプロダクトももうそれほどないのでは、と思うのですが、 もしまだ残っているならこういう選択肢もあります。移行ガイドもあります。

Migration from ActiveAndroid · ReActiveAndroid

IoT, Mobile device対応を謳うObjectBoxは性能も凄い

ObjectBox - Edge Database for Mobile and IoT

EventBusやgreenDAOの開発チームが手掛ける新しいデータベースシステム。 Realmとアプローチが似ているが、 ObjectBox is smaller than 1MB とあるとおりコアエンジンが軽量であることや、ベンチマーク結果の優位性などをうたい文句にしています。 今後、ObjectBox Syncというソリューションを提供していくようにうかがえます。これもRealmと似ていますが、さてうまくいくでしょうか。

また、現時点では素晴らしいスコアですが、今後もそれを維持できるか?も鍵になりそうです。 どちらにせよ今後が楽しみなORMと言えます。

coroutines対応は現時点でRoomのみ

Room Persistence Library  |  Android Developers

今回、機能比較としてcoroutines対応しているかどうかを調査しました。 (サードパーティではなく、オフィシャルとして提供があるかどうか) 結果、Roomのみがサポートしているという状況のようです。

とはいえcoroutines分かっているなら自分で実装もできそうです。僕はあんまり分かりませんけどね!

参考: Callback形式のものをCoroutinesに対応する – Kenji Abe – Medium

3行でまとめ

  • この記事連載を行うきっかけとなったRealmが首位を明け渡した
  • Roomの性能の高さと純正の安心感、そしてObjectBoxの将来性に期待がかかる
  • でも速さだけがORMの良さを決めるってものでもない(数年ぶり3度めの宣言)
  • 某氏曰く「本当にその案件がORM使う必要あるのか考えたほうが良い」
    • 3行とは・・・

終わりに

丸3年ぶりに本記事を執筆しました(Qiitaが生き残ってて俺は嬉しいよ)。 前回からの間に、GoogleがRoomを出したためにずいぶん様相が変わってしまいました。 また、Androidを取り巻く環境も大きく変わり、androidxへの移行、Kotlinがより使われるようになったというトピックがあります。 結果、coroutineやKotlin言語との親和性に気をかけているORMが出ていたりと、本ベンチマークでは言及できないポイントも多くありました(ベンチ用コードはJavaベースで保守してますが、結局Kotlinにする前に役目を終えそうです)。 ずっと言い続けていることですが、「速さだけがORMの良さを決めるってものでもない」のです。開発体験の良さとか、長期にわたる運用への安心感とか、そういうのは数値化の難しいポイントです。

私は2014年版の記事において以下のように書きました。

ところが、AndroidのORMは群雄割拠で、どれを選んだらいいのかわからないという問題があります。 あと、「○○が良い」「いや○○が今主流」という話が風のうわさで流れてくるのですが、実際のところどうなの???というのが分からなかったりします。

どのORMを使ったら良いのか、という問題は、個人的に大疑問だったのですが、今回Advent Calendarをやるということで良い機会でしたので調べることにしました。

そういう意味において、GoogleがRoomを出したことで、私たちの(今となっては私個人の)問題は解決されたように見受けられます。 よって、今回をもってORMの比較検討を終えることとします。

(ただ、Ormaのスコアを検証できなかったのは心残りなので、いつか解決されたタイミングで再検証したいかもしれない(フラグか?(いやそれはない)))