こんにちわ。luckinです。
KLabでは今までデータベース基盤としてSqliteを採用していましたが、パフォーマンス面で問題を抱えることが多くありました。
今回はSqliteでのデータベース基盤での課題やそれを解決するために大規模開発プロジェクトにMasterMemoryを導入した際のノウハウや実際に入れてみての使い勝手やMasterMemoryがパフォーマンス、特にメモリサイズにどれくらい影響を及ぼすのかについてご紹介します。
KLabでは長らくSqliteでのデータベース基盤を採用し、多くのプロジェクトを支えてきました。
しかし、近年ではプロダクトのパフォーマンス向上やゲーム内コンテンツの増加に伴いSqliteがボトルネックの要因になることが増えてきました。
Sqlite単体では十分なパフォーマンスを発揮しています。ではどこがボトルネックになるのでしょうか?
KLabではレコードを取り出す際のアロケーションが大きな問題となりました。
C#は静的型付け言語であり、Sqliteの柔軟なデータ構造に適していません。
レコードを取り出すにはBoxingを覚悟した柔軟なオブジェクトで取り出すか、テーブルスキーマに準じた型定義を行ったオブジェクトで取り出す必要があります。
KLabでは後者を選択しており、このオブジェクトのアロケーションがレコード毎に発生します。
またレコード内の情報が全て利用される訳ではなく、一部分の情報のみが利用されることが多くあります。
このC#とSqliteの相性の悪さによるアロケーションを回避するため以下の条件を満たすデータベース基盤を模索しました。
object
を使わないC#でおなじみneueccさんこと河合 宜文さんが実装に携わっているオープンソースのオンメモリマスタデータ基盤となります。
ファイルのフォーマットとしてはmsgpackになっており仕組みは非常にシンプルでソートしたリストを二分探索するだけとなっています。
ではどこが他のDB基盤と異なるのかというとそれは圧倒的アロケーションの少なさとなります。
特にUnityではIncrementalGCが導入されたものの、引き続きアロケーション削減とGCによるスパイク軽減は重要な課題であり、MasterMemoryはこの面で圧倒的パフォーマンスを発揮します。
※ Cysharp様のMasterMemoryリポジトリから引用
まずプロジェクト導入するにあたって事前に既存のプロジェクトからフォークしたプロジェクトにてMasterMemoryの検証を行いました。
その結果を下記にまとめます。
下記のスキーマを持ったテーブルにレコードを265件InsertしSelectを100回行う。
その後取り出したデータをMonoのマネージドメモリ上に展開し、C#上でアクセス可能になるまでの速度と総アロケーション量を検証する。
テーブルスキーマ
CREATE TABLE card(
rank INTEGER NOT NULL,
id INTEGER NOT NULL,
character_m_id INTEGER NOT NULL,
order_no INTEGER NOT NULL,
card_rarity_type INTEGER NOT NULL,
card_attribute INTEGER NOT NULL,
role INTEGER NOT NULL,
thumbnail_asset_path TEXT NOT NULL,
autograph_image_asset_path TEXT NOT NULL,
at_gacha INTEGER NOT NULL,
at_event INTEGER NOT NULL,
training_m_id INTEGER NOT NULL,
voice_path TEXT NOT NULL,
point INTEGER NOT NULL,
exchange_item_id INTEGER NOT NULL,
role_effect_master_id INTEGER NOT NULL,
skill_slot INTEGER NOT NULL,
max_skill_slot INTEGER NOT NULL,
analysis_id INTEGER NOT NULL,
PRIMARY KEY (id)
);
speed | allocation | |
---|---|---|
sqlite3 + klbvfs | 351ms | 8.2MB |
sqlite3 + klbvfs + 独自C#キャッシュ機構 | 44ms | 188.3KB |
MasterMemory | 8.17ms | 0B |
シンプルな検証ではありますが、既存のデータベース基盤と比較して十分な速度が出ていることがわかります。
特に今回はキャッシュヒットしやすい検証環境でしたが、それでもキャッシュ機構を用いた結果より5倍ほどの向上がみられました。
導入プロジェクトではDBとMasterMemoryとSqliteを使い分ける形で導入しています。
導入プロジェクトでは膨大なアセットを扱う関係でパス情報が増えるため全てをMasterMemoryに寄せるのはメモリサイズ的に現実的ではないためです。
公式から提供されているもの
独自に用意したもの
非常に高速なためプロダクト全体のクオリティを担保しやすい
APIがわかりやすいのでメンバーへの学習コストがかなり少なく済んだ
懸念だったマネージドメモリのサイズを適切な範囲内に収められたこと
枯れた技術ではないのでバグが見つかる 以下バグの内容
定期的にオンメモリ上のサイズをMemoryProfileでチェックする必要がある(旗振り役必須)
型+bool
のフィールドを持つようにKLabでは品質の高いプロダクトを実装する上でModelは不変であるという考え方を取り入れています。これは実装を単純明快にすると引き換えにパフォーマンスを犠牲にするものでした。
今回MasterMemoryを導入することで実装を単純明快にしつつ、C#のビジネスロジックレイヤーでパフォーマンスを意識する必要をほとんど無くすことができたと思っています。
最後に、この素晴らしいライブラリを作ってくださったneueccさんとリポジトリのコントリビューターの皆様に感謝を
KLabのゲーム開発・運用で培われた技術や挑戦とそのノウハウを発信します。
合わせて読みたい
KLabのゲーム開発・運用で培われた技術や挑戦とそのノウハウを発信します。