gormでPreload周りのメモリ使用量とかを比較してみた

はじめに

gamelinks007.hatenablog.com

この記事は、上記の記事でgormのバージョンを上げる際にPreload周りの挙動を調べてた時のあれこれをまとめたものです。

ことの発端

gorm v2へのアップデート時に、一部処理でメモリリークらしき挙動が確認されたのがことの発端。 で、色々調べてた感じでgorm v2でPrelaod関連でmap[string]interface{}がそこそこ使われてたのでこの辺でメモリリークしてるのかもと思いPreload周りのメモリ使用量を調査してみました。

ちなみに、メモリリークしてたのは変なクエリをコード内で作成してて、それが原因でテーブル内のレコードを全件取得してたのが原因でした……。

前提条件

gorm v1とv2との間でPreloadを多用した際にメモリの使用量に差が出るのかを調査します。

メモリ使用量などはPrometheusやGrafanaを使いグラフで監視することにしました。

また最低限度の実装でも再現するのかを確認したいので使用するライブラリは以下のものだけにしています。

  • gin
  • gorm(v1またはv2)
  • sqlite3

またベンチマークにはApache Benchを使用し、ベンチマーク用のプログラムとしては以下を使用しました。

またデータの数も影響する可能性を考えてデータは以下のようなものを使用した。

  • users
    • 件数:10000
    • テーブル構成:create table users(id integer primary_key, name text not null);
  • tweets
    • 件数:10000
    • テーブル構成:create table tweets(id integer primary_key, content text not null, user_id integer);

なお、tweetsのuser_idはランダムにしているためv1とv2とで取得する件数は若干違います(ただしその差は2件のみ)

調査結果

f:id:gamelinks007:20211027215459p:plain

以下のリクエストをそれぞれのエンドポイントに投げたグラフが上のものです。

ab -n 10000 -c 50 <エンドポイント>

なお、v1とv2それぞれのリクエスト成功数は以下の通りです

v1: Total of 3305 requests completed(17:49に開始)
v2: Total of 5588 requests completed(17:50に開始)

傾向としては - v1 - 全体的にヒープ上のメモリを使う - ヒープのオブジェクト数はv2より少ない - GCの呼び出し回数はv2より多い - v2 - 全体的にヒープ上のメモリはv1より使用しない - ヒープ上のオブジェクト数はv1に比べてかなり多い - GCの呼び出し回数はv1より少ない

総合するとv2のほうが総合的なパフォーマンスは上っぽい。リクエストをさばいた量も多く、且つヒープ上のメモリもv1に比べると少ないのも評価できるポイント。

ただv2はGC自体の呼び出し回数が少ないので他のクエリも同時に捌いている状態だとパフォーマンスが悪くなる可能性があるかもくらい……?

おわりに

トータルで見るとgorm v2のほうがパフォーマンスが上なので、これからGoでWebアプリ開発とかするのであればgorm v2使うほうが良さそう。