パフォーマンスベンチマークとチューニング
Tip
いつこのドキュメントを読むべきか:イベントのスループットやレイテンシの数値に関心がある場合、デプロイ前のキャパシティ評価、バージョンアップグレード前後のパフォーマンスリグレッションを比較したい場合に読んでください。必要がなければ読み飛ばしても、他の機能の利用には影響しません。
実測ベンチマーク(2026-10-05、Python 3.13.14)
同一マシン上での前後2回の実測(ウォームアップ + 複数回のサンプリングによるパーセンタイル算出)、NoneBot2 2.5.0 は独立環境の対照。
すべての数値はリポジトリ同梱のベンチマークで再現可能:python tests/performance/benchmark.py。
イベントディスパッチ(ops/s、値が高いほど良い)
| ベンチマーク項目 | ErisPulse | NoneBot2 2.5.0 | 比較 |
|---|---|---|---|
| バス単一ハンドラ逐次レイテンシ | 9,189 | 3,021 | 3.0× |
| バスフラッドスループット(1000 イベント/ラウンド) | 9,877 | 2,055 | 4.8× |
| 10 ハンドラ/イベント スループット | 4,309 | 435 | 9.9× |
| コマンドヒット(/コマンド) | 2,165 | 677 | 3.2× |
| コマンドミス(コマンド以外のメッセージ) | 10,818 | 921 | 11.7× |
| メッセージブリッジ単一ハンドラ(デフォルト全パイプライン) | 2,804 | 2,144 | 1.3× |
その他サブシステムの能力(最適化後)
| ベンチマーク項目 | ops/s | ベンチマーク項目 | 数値 |
|---|---|---|---|
| getConfig ホット読み取り | 1,086,957 | ORM 単一 create | 407/s |
| KV aset(sqlite) | 1,659 | GET /health(ASGI) | 1,188/s |
| KV aget(sqlite) | 3,333 | module.call RPC | 148,905/s |
| エンドツーエンドメッセージ往復 | 298 | 混合負荷 | 142/s |
起動時間
| フェーズ | ErisPulse | NoneBot2 |
|---|---|---|
| インタプリタ空転対照 | 59.5ms | — |
| コールドインポート | 241.7ms | 249.6ms |
| sdk.init / NoneBot init | 30.8ms | 162.3ms |
ベンチマークの実行とリグレッション比較
# クイックモード(サンプル数は約1/4、スモークテスト用)
python tests/performance/benchmark.py --quick
# 完全なベースラインをファイルに保存(8スイート:config/event/storage/send/router/module/overall/startup)
python tests/performance/benchmark.py --json baseline.json
# 旧ベースラインとの比較(差分が±5%の閾値内ならノイズとみなす)
python tests/performance/benchmark.py --compare baseline.json
# HTMLレポート / ストレージスイートのバックエンド指定
python tests/performance/benchmark.py --html report.html
python tests/performance/benchmark.py --backend mysql
CI に関する注意:ベンチマークの数値はマシンによって変動するため、
--compareの ±5% 閾値は同一マシンでの前後比較(アップグレード検証)にのみ適しており、マシンをまたぐゲート判定には適していません。
チューニングのポイント
- メッセージブリッジの全経路におけるボトルネックはセッション記録:デフォルトで transcript 記録が有効(2.9 以降はメモリバッファ + バックグラウンドでの一括永続化);監査要件がない場合は無効化可能で、全経路のスループットは約 2,804 → 3,774 ops/s に向上
- GC 管理:
ErisPulse.frameworkの proactive_gc 設定グループ(interval / full_every / memory_growth_mb /idle_only)——イベントのピーク時には自動的に GC をスキップ;init フェーズでのgc.freeze()は組み込み済み - ストレージバックエンド:ローカル単体の sqlite(WAL)で十分;マルチインスタンスでデータ層を共有する場合は mysql / postgres に切り替え(ストレージバックエンド を参照)
- ホットパスは最適化組み込み済み(2.9 以降、設定不要):transcript の一括永続化、i18n キャッシュ、ログレベルガード、マッチャーの事前コンパイル、設定の遅延書き込みと統一フラッシュ
- コマンド制御状態の上限:クールダウン/レート制限/クォータの状態テーブルは上限 4096 件(超過時は最も古いものから削除)、超大規模グループ向けのデプロイでも調整不要
メモリ診断
「実行するほどにメモリを多く消費する」問題を調査する際、疑わしい処理の前後にラベル付きのスナップショットを打つことで、同名のラベルが自動的に増分(RSS + tracemalloc 両指標)を計算します。
from ErisPulse.runtime import log_snapshot, snapshot
class MyModule(BaseModule):
async def on_load(self, event):
snapshot("MyModule.load") # dict(ラベルによる RSS delta 含む)を取得して返す
async def on_unload(self, event):
log_snapshot("MyModule.unload") # 同じ口径で取得し TRACE レベルのログに記録
snapshot(label)はマシンで読み取れる dict を返し、アサーションや報告に適しています。log_snapshot(label)は TRACE レベルのログに直接書き込み、一時的な調査に適しています。- label は容量制限付きで、呼び出し回数に応じてメモリが漏れることはありません。
/metricsエンドポイントのプロセス指標と併用することで、長期的な傾向を観察できます。 - モジュールのアンロード後に残る増加は、通常、所有権の欠如を示しています(所有権システムの
bind_ownerタスク項目を参照)。
関連ドキュメント
- 実行メトリクスの観測:
/health(インスタンス識別 / イベントカウント)と/metrics(Prometheus テキスト、ErisPulse.server.metrics_enabledが有効な場合)については、デプロイガイドを参照してください