Edited at

CoreDataをRealmに移行し高速化できたのでベンチマークで性能を比較してみた。

More than 3 years have passed since last update.

追記: Fetch結果について補足しました。


AplosというTwitterクライアントでCoreDataを使用していたのですが、CoreDataが基本重いに加えてデータベースが肥大化するにつれて指数関数的にどんどん重くなるという問題があり頭を抱えていました。そこで以前から気になっていたRealmを試してみたら実用に耐えられるレベルと判断できたので、思い切ってRealmに移行してみました。

(※まだ全ての実装は終わっていないのですが、このまま問題がなければAplosのバージョン2.0で完全移行する予定です。)

Realmを採用するかを検討するにあたりベンチマークも取ってみました。Realm公式にもベンチマーク比較はありますが、今回はTwitterクライアントで実際に使用している設計とテストデータでベンチマークを取っていますので、より現実的な結果が反映されていると思います。

※この記事はRealmのバージョン0.88.0について記載しています。Relamの開発は活発なので試す場合はバージョンに注意してください。


Realmの基礎知識

Realm(レルム)はモバイル向けに開発されたデータベースです。コア部分にはTightDBというC++で実装されたストレージエンジンが使用されています。

公式

その他


ベンチマーク


条件


  • テストデータはTwitter REST APIで取得したTweetを扱います。


  • TweetUserはユニークです。


    • CoreDataでは追加毎にフェッチし一意性を保ちます。

    • RealmはPrimaryKey(主キー)がサポートされていますが、様々な理由(次回の記事で補足)によりCoreDataと同じくフェッチを行っています。



  • Aplosの仕様で特殊なマルチユーザ対応などその他様々な条件も絡みあっているので、単純にTweetデータのみを扱った結果ではありません。(ただそこまで影響度が高いわけでもありません。)



テーブル定義


1. CoreData1

TwitterのJSON通り(リレーションも適切に)

model.png


2. CoreData2

リレーションを極力排除、カラムも整理し最適化

optimized-the-model.png


3. Realm

CoreData1をそのままRealmで定義


ベンチマーク結果


Insert


iPhone4s

aeb4a707c4a75eea529984a06645c9f6.png


その他の結果

100件(4s)
1000件(4s)

100件(6+)
1000件(6+)

100件(Sim)
1000件(Sim)
1万件(Sim)

CoreData1
1.382
33.974

0.309
7.168

0.158
4.057
-

CoreData2
0.938
19.842

0.195
3.917

0.118
2.197
281.451

Realm
0.576
5.800

0.102
1.031

0.075
0.664
7.431


Fetch



  • TweetをIDでソートしフェッチ


iPhone4s

117b8411f878b9b2a2a64cbe65beb19b.png


その他の結果

1000件(4s)
1万件(4s)

1000件(6+)
1万件(6+)

1000件(Sim)
1万件(Sim)
10万件(Sim)

CoreData1
0.003
-

0.001
-

0.000
-
-

CoreData2
0.003
-

0.001
-

0.000
-
-

Realm
0.064
1.541

0.012
0.155

0.006
0.125
6.677

- 単位は秒でXCTestのパフォーマンス測定用メソッドの平均処理速度を記載。

- 4sはiPhone4s、6+はiPhone6 Plus、Simはシミュレータ。'-'は都合により未測定。


考察


Insert


CoreData


  • リレーションを減らすことによってパフォーマンスが向上した。

  • データ件数に対する処理時間の増加率はリレーションとは関連なさそう。データ件数が10倍になった場合、10倍以上の処理時間が増加する傾向。


Realm


  • CoreData1の様なリレーションが多い設計でもパフォーマンスはCoreDataより良く、平均して6倍程度速かった。

  • データ件数が10倍になったら処理時間も10倍と比例する。


Fetch


  • RealmのフェッチはCoreDataより遅い。 おそらくインデックスが定義できないのでソートのコストが高いと予想。

[訂正]

- ベンチマーク結果は変わらないのですが、当初Realmが遅い原因がソートコストと記載していましたが今回の結果には関係なかったです。純粋にオブジェクトの取得部分の比較になります。具体的には以下のコードでベンチマークを取っています。

- 前提としてですが、フェッチ条件が複雑な場合を想定しています。複雑なフェッチを一旦バックグラウンドで実行し、メインスレッドでPrimaryKeyの値を利用して再度フェッチするという方法です。フェッチ条件が複雑でなければそのままの条件をメインスレッドでフェッチした方が高速かもしれません。

- RealmではCoreDataのObjectIDのような仕組みがまだ提供されていないので、CoreDataと比べるとどうしてもコストが高くなってしまいます。(十分高速ですが)


Realm

RLMResults *results;

if (!wself.isCancelled && [values count] > 0 && resultClass && primaryKey) {
results = [resultClass objectsInRealm:[wself realm]
where:@"%K IN %@", primaryKey, values];
}


CoreData

NSMutableArray *fetchResults = [NSMutableArray arrayWithCapacity:[ids count]];

for (NSManagedObjectID *objID in ids) {
NSError *error = nil;
NSManagedObject *obj = [strongSelf.mainContext existingObjectWithID:objID error:&error];
if (obj == nil || error) {
if (completion) completion(strongSelf, nil, error);
return;
}
[fetchResults addObject:obj];
}


まとめ


  • 今回の条件では6倍程度の高速化ができました。Realmが現在の設計(CoreData1)を変更することなくパフォーマンスが向上できたことが、採用に踏み切れた一番の決め手でした。速いは正義です。

  • CoreData2の設計にすればより速くなるかもしれませんが、どうしてもコード量が増えてしまうので避けたいところです。

  • CoreDataで1000件INSERTするのに100件x10の方が速い場合もあり、Aplosではいろいろ試して良かったものを採用しています。もしかしたらCoreDataの作法としてInsertや一意性の確保などで別のより良い方法があるかもしれませんが、何も考えなくても速いRealmは素晴らしいと思います。

  • そんな素敵なRealmもいいところばかりではなく様々な問題があります。長くなったので詳しくは次の記事CoreDataからRealmに移行してわかったメリット/デメリットをご覧ください。