giocci の性能計測を行い、処理時間を CSV で出力するためのベンチマークプロジェクトです。
通信にかかる時間の基準値として、giocci の計測前に ping 応答時間を計測します。
- 宛先:
--ping-targetsオプションで指定(デフォルト: 127.0.0.1) - 回数:
--ping-countオプションで指定(デフォルト: 5回) - CSV への記録方法: 別スキーマ
- RTT が取得できない場合は
success=false、elapsed_msは空、errorに詳細を記録
giocci の主要処理として、lib/giocci_bench/samples/ 配下のベンチマーク用モジュールを転送して計測します。
- デフォルトは
GiocciBench.Samples.Addモジュール(引数:[1, 2]) - 処理時間は「呼び出しからリターンを得るまでの実測時間」を計測
- 計測は
mix giocci_bench.singleとして呼び出せる - ベンチ実行時点の環境情報はメタデータファイルに記録
- OS, Elixir バージョン, Erlang/OTP バージョン
- CPU コア数
実行フロー:
- ping 計測 - 通信にかかる時間の基準値を取得(
--no-pingで無効化可能) - prepare - 計測対象に応じて事前に必要な giocci 関数を実行
- ウォームアップ - 初回実行の影響を除外するため指定回数実行
- イテレーション計測 - 指定回数の計測を実行し、各実行時間を記録
計測項目:
register_clientsave_module- 計測前に
register_clientを事前実行
- 計測前に
exec_func- 計測前に
register_clientとsave_moduleを事前実行
- 計測前に
比較用に、同一のサンプルモジュールをローカルで直接実行する local_exec を計測できます。
- 計測は
mix giocci_bench.localとして呼び出せる - ローカル比較計測では ping 計測を行わない
giocci の連続処理として、register_client → save_module → exec_func を順に実行し、
シナリオ全体の処理時間を計測します。
- 計測は
mix giocci_bench.sequenceとして呼び出せる register_clientの呼び出し開始からexec_funcの返却取得までを計測- 計測対象の mfargs は
exec_funcのみを meta.json に記録
計測セッションごとにディレクトリを作成し、メタデータと計測結果を分離します。
単体計測(mix giocci_bench.single)の例:
giocci_bench_output/
session_20260309-140530-single/
meta.json # 計測セッションのメタデータ
ping.csv # ping 計測結果
register_client.csv # 単体計測結果(ケースごとに分割)
register_client_os_info_free.csv # OS情報(--os-info 指定時のみ)
register_client_os_info_proc_stat.csv # OS情報(--os-info 指定時のみ)
save_module.csv
exec_func.csv
ローカル比較計測(mix giocci_bench.local)の例:
giocci_bench_output/
session_20260309-140530-local/
meta.json
local_exec.csv
シーケンス計測(mix giocci_bench.sequence)の例:
giocci_bench_output/
session_20260310-101530-sequence/
meta.json
ping.csv
sequence.csv # シーケンス計測結果
- 出力先ディレクトリのデフォルトは
giocci_bench_output - ディレクトリの命名規則は
session_<run_id>-<task>[-<title>]run_idは実行開始時刻(UTC)のYYYYMMDD-HHMMSS形式taskは実行したMixタスク名- 単体計測のディレクトリ名は
session_<run_id>-single - シーケンス計測のディレクトリ名は
session_<run_id>-sequence - ローカル比較計測のディレクトリ名は
session_<run_id>-local
- 単体計測のディレクトリ名は
--title指定時はsession_<run_id>-<task>-<title>形式になり、/と\は_に置換
- 単体計測結果は
case_idごとに別ファイルに分割(例:register_client.csv,save_module.csv) - シーケンス計測結果は
sequence.csvに出力 - ローカル比較計測結果は
local_exec.csvに出力 --os-info指定時は実行モードに応じた OS 情報 CSV(*_os_info_free.csv,*_os_info_proc_stat.csv)を同じディレクトリに保存
計測セッション全体の環境情報と、計測ケース説明を JSON で記録します。
casesには各ケースの実行時の{module, function, args}をinspect/1で文字列化した値が記録されます。measure_mfargsには設定/実行時に使った計測対象 mfargs(config/config.exsのmeasure_mfargsなど)がinspect/1で記録されます。- giocci のケースは引数末尾のオプション(
timeoutとmeasure_to)も含まれます。 - 単体計測では
measure_toは meta.json ではnilで記録されますが、実行時には計測用 PID が注入されます。 measure_mfargsには計測対象として設定された mfargs がinspect/1で文字列化されて記録されます(cases内の個別ケースの mfargs とは異なります)。--title指定時はtitleフィールドが meta.json に追加されます。
{
"run_id": "20260309-140530",
"started_at": "2026-02-17T10:00:00Z",
"elixir_version": "1.14.0",
"otp_version": "24.0",
"os_type": "unix-linux",
"system_arch": "x86_64-pc-linux-gnu",
"cpu_cores": 4,
"measure_mfargs": "{GiocciBench.Samples.Sieve, :run, [[1000000]]}",
"cases": {
"register_client": "{Giocci, :register_client, [\"giocci_relay\", [timeout: 5000, measure_to: nil]]}",
"save_module": "{Giocci, :save_module, [\"giocci_relay\", GiocciBench.Samples.Sieve, [timeout: 5000, measure_to: nil]]}",
"exec_func": "{Giocci, :exec_func, [\"giocci_relay\", {GiocciBench.Samples.Sieve, :run, [[1000000]]}, [timeout: 5000, measure_to: nil]]}"
}
}ローカル比較計測では cases に local_exec の mfargs を 1 件だけ記録します。
{
"measure_mfargs": "{GiocciBench.Samples.Sieve, :run, [[1000000]]}",
"cases": {
"local_exec": "{GiocciBench.Samples.Sieve, :run, [[1000000]]}"
}
}シーケンス計測では cases に exec_func の mfargs を 1 件だけ記録します。
シーケンス計測は通信内訳の計測を行わないため、measure_to は利用しません。
{
"measure_mfargs": "{GiocciBench.Samples.Sieve, :run, [[1000000]]}",
"cases": {
"sequence": "{Giocci, :exec_func, [\"giocci_relay\", {GiocciBench.Samples.Sieve, :run, [[1000000]]}, [timeout: 5000]]}"
}
}ping の計測結果を記録します。session_<run_id>-ping ディレクトリ内に保存されます。
| column | type | description |
|---|---|---|
| run_id | string | 計測セッション識別子 (実行開始時刻(UTC)の YYYYMMDD-HHMMSS) |
| target | string | 宛先 IP アドレス |
| iteration | integer | 計測回数の通し番号 (1..count) |
| elapsed_ms | float | RTT ( |
| success | boolean | 成功フラグ |
| error | string | エラーメッセージ(成功時は空) |
- 計測結果は
case_idごとに別ファイルに分割 - ファイル名は計測ケースの
case_idと同じ - 1 行 1 計測結果
- UTF-8, 改行は LF
- 環境情報は
meta.jsonに記録するため CSV に含めない - 実行時の mfargs は
meta.jsonのcasesマップに記録 - 計算済み通信時間はデフォルトで出力
- 計算元タイムスタンプは
--include-timestamps指定時のみ出力(デフォルト: 非出力)
| column | type | description |
|---|---|---|
| run_id | string | 計測セッション識別子 (実行開始時刻(UTC)の YYYYMMDD-HHMMSS) |
| case_id | string | 計測ケース識別子 (register_client, save_module, exec_func) |
| iteration | integer | 計測回数の通し番号 (1..iterations) |
| elapsed_ms | float | クライアント側での呼び出しからリターンまでの時間 ( |
| function_elapsed_ms | float | 関数の処理時間 (exec_func のみ、サンプルモジュールが GiocciBench.Samples.BenchmarkBehaviour behaviour を実装して返す) |
| warmup | integer | 実行した warmup 回数 |
| client_to_relay | float | クライアント→リレー通信時間 ( |
| relay_to_client | float | リレー→クライアント通信時間 ( |
| relay_to_engine | float | リレー→エンジン通信時間 ( |
| engine_to_relay | float | エンジン→リレー通信時間 ( |
| client_to_engine | float | クライアント→エンジン通信時間 ( |
| engine_to_client | float | エンジン→クライアント通信時間 ( |
計測には Elixir 標準の System.os_time/1 を使用します。
--include-timestamps 指定時は、以下の計算元タイムスタンプ列($ms$)も追加で出力されます。
client_send_timestamp_to_relayrelay_recv_timestamp_from_clientrelay_send_timestamp_to_clientclient_recv_timestamp_from_relayrelay_send_timestamp_to_engineengine_recv_timestamp_from_relayengine_send_timestamp_to_relayrelay_recv_timestamp_from_engineclient_send_timestamp_to_engineengine_recv_timestamp_from_clientengine_send_timestamp_to_clientclient_recv_timestamp_from_engine
- 計測結果は
local_exec.csvに出力 case_idは常にlocal_exec- 1 行 1 計測結果
- UTF-8, 改行は LF
| column | type | description |
|---|---|---|
| run_id | string | 計測セッション識別子 (実行開始時刻(UTC)の YYYYMMDD-HHMMSS) |
| case_id | string | 固定値 local_exec
|
| iteration | integer | 計測回数の通し番号 (1..iterations) |
| elapsed_ms | float | ローカル呼び出しからリターンまでの時間 ( |
| function_elapsed_ms | float | 関数の処理時間 (GiocciBench.Samples.BenchmarkBehaviour behaviour を実装して返す) |
| warmup | integer | 実行した warmup 回数 |
| client_to_relay | float | 空 |
| relay_to_client | float | 空 |
| relay_to_engine | float | 空 |
| engine_to_relay | float | 空 |
| client_to_engine | float | 空 |
| engine_to_client | float | 空 |
- 計測結果は
sequence.csvに出力 case_idは常にsequence- 1 行 1 計測結果
- UTF-8, 改行は LF
- 環境情報は
meta.jsonに記録するため CSV に含めない - 実行時の mfargs は
meta.jsonのcasesにexec_funcのみを記録
| column | type | description |
|---|---|---|
| run_id | string | 計測セッション識別子 (実行開始時刻(UTC)の YYYYMMDD-HHMMSS) |
| case_id | string | 固定値 sequence
|
| iteration | integer | 計測回数の通し番号 (1..iterations) |
| elapsed_ms | float | シナリオ全体の処理時間 ( |
| function_elapsed_ms | float | 関数の処理時間 (exec_func の返却値から取得) |
| warmup | integer | 実行した warmup 回数 |
| error | string | 失敗時は {:error, reason} の reason を inspect/1 した値(成功時は空) |
- 各ケースに対して平均・中央値・標準偏差・分散を算出
- 集計結果は別途レポートや図に反映
計測対象の mfargs は config/config.exs の config :giocci_bench で設定できます。
config :giocci_bench,
# {module, func, args}
# IMPORTANT: args is a list passed to apply/3.
# If run/1 expects a list argument, wrap it as [[...]].
measure_mfargs: {GiocciBench.Samples.Sieve, :run, [[1_000_000]]}measure_mfargs- 単体計測 / ローカル比較計測 / シーケンス計測で共通して使う mfargs
- 実行時オプション
:mfargs(内部APIからrun/1を直接呼ぶ場合) measure_mfargs- デフォルト値:
{GiocciBench.Samples.Add, :run, [[1, 2]]}
# デフォルト設定で実行(ping: 127.0.0.1 へ 5 回)
mix giocci_bench.single
# ping を無効化
mix giocci_bench.single --no-ping
# ping ターゲットをカスタマイズ
mix giocci_bench.single --ping-targets "127.0.0.1,192.168.0.101,192.168.0.102"
# ping 回数をカスタマイズ
mix giocci_bench.single --ping-count 10
# 複数オプションの組み合わせ
mix giocci_bench.single --ping-targets "127.0.0.1,8.8.8.8" --ping-count 3 --iterations 10
# 特定のケースのみ計測
mix giocci_bench.single --cases "register_client,save_module"
# セッションタイトルを付けて実行(出力ディレクトリ名と meta.json に反映)
mix giocci_bench.single --title "nightly"
# ローカル比較計測(local_exec のみ)
mix giocci_bench.local
# 計算元タイムスタンプ列も出力
mix giocci_bench.single --include-timestamps
# OS情報(CPU/メモリ)も取得(100ms周期、warmup後〜計測完了まで)
mix giocci_bench.single --os-info
# 計測完了後に可視化して report.html を生成
mix giocci_bench.single --visualize
# シーケンス計測を実行
mix giocci_bench.sequence
# シーケンス計測で ping を無効化
mix giocci_bench.sequence --no-ping
# シーケンス計測で反復回数を変更
mix giocci_bench.sequence --iterations 10
# ローカル比較計測 + 計測完了後の可視化
mix giocci_bench.local --visualize--no-ping- ping 計測を無効化(デフォルト: 有効)--ping-targets- ping ターゲット(カンマ区切り)(デフォルト: 127.0.0.1)--ping-count- 各ターゲットへの ping 回数(デフォルト: 5)
--relay- Relay 名(デフォルト: GIOCCI_RELAY 環境変数または "giocci_relay")--warmup- ケースごとのウォームアップ回数(デフォルト: 1)--iterations- ケースごとの計測回数(デフォルト: 5)--timeout-ms- Giocci 呼び出しのタイムアウト (ミリ秒)(デフォルト: 5000)--out-dir- CSV 出力ディレクトリ(デフォルト: giocci_bench_output)--title- セッションタイトル(出力ディレクトリ名末尾と meta.json のtitleに反映)--cases- 計測するケース(カンマ区切り: register_client, save_module, exec_func)--no-ping- ping 計測を無効化(デフォルト: 有効)--ping-targets- ping ターゲット(カンマ区切り)(デフォルト: 127.0.0.1)--ping-count- 各ターゲットへの ping 回数(デフォルト: 5)--include-timestamps- 計算元タイムスタンプ列をCSVに含める(デフォルト: 無効)--os-info- OS情報計測を有効化(100ms周期、warmup後〜計測完了まで、デフォルト: 無効)--visualize- 計測完了後に最新セッションを可視化しreport.htmlを生成(デフォルト: 無効)
--warmup- ケースごとのウォームアップ回数(デフォルト: 1)--iterations- ケースごとの計測回数(デフォルト: 5)--out-dir- CSV 出力ディレクトリ(デフォルト: giocci_bench_output)--title- セッションタイトル(出力ディレクトリ名末尾と meta.json のtitleに反映)--include-timestamps- 計算元タイムスタンプ列をCSVに含める(デフォルト: 無効)--os-info- OS情報計測を有効化(100ms周期、warmup後〜計測完了まで、デフォルト: 無効)--visualize- 計測完了後に最新セッションを可視化しreport.htmlを生成(デフォルト: 無効)
--relay- Relay 名(デフォルト: GIOCCI_RELAY 環境変数または "giocci_relay")--warmup- シナリオのウォームアップ回数(デフォルト: 1)--iterations- シナリオの計測回数(デフォルト: 5)--timeout-ms- Giocci 呼び出しのタイムアウト (ミリ秒)(デフォルト: 5000)--out-dir- CSV 出力ディレクトリ(デフォルト: giocci_bench_output)--title- セッションタイトル(出力ディレクトリ名末尾と meta.json のtitleに反映)--no-ping- ping 計測を無効化(デフォルト: 有効)--ping-targets- ping ターゲット(カンマ区切り)(デフォルト: 127.0.0.1)--ping-count- 各ターゲットへの ping 回数(デフォルト: 5)--os-info- OS情報計測を有効化(100ms周期、warmup後〜計測完了まで、デフォルト: 無効)--visualize- 計測完了後に最新セッションを可視化しreport.htmlを生成(デフォルト: 無効)
scripts/run_bench_matrix.sh を使うと、次の組み合わせをまとめて実行できます。
- Mix タスク:
single,sequence - endpoint 設定:
ZENOH_EP_CONFIG1(必須),ZENOH_EP_CONFIG2(任意) - アプリ設定:
sieve,big_beam,cpu_eater,memory_eater
config/config.exs の measure_mfargs は app ごとに自動で書き換えられ、実行終了時に元の内容へ復元されます。
# 1) 実行権限を付与(初回のみ)
chmod +x scripts/run_bench_matrix.sh
# 2) endpoint を設定して実行(config1 のみ)
ZENOH_EP_CONFIG1="tcp/192.168.0.10:7447" \
./scripts/run_bench_matrix.sh
# 3) endpoint を2つ設定して実行(config1 + config2)
ZENOH_EP_CONFIG1="tcp/192.168.0.10:7447" \
ZENOH_EP_CONFIG2="tcp/192.168.0.11:7447" \
./scripts/run_bench_matrix.sh各実行は内部的に次の形式で呼び出されます。
ZENOHD_CONNECT_ENDPOINTS="<EP>" mix giocci_bench.<single|sequence> --os-info --iterations 100 --title <configX-appY>このとき appY は次の対応で付与されます。
appA->sieveappB->big_beamappC->cpu_eaterappD->memory_eater
本機能はGitHub Copilotで実装されています(??
# 最新セッションの CSV をグラフ化して report.html を生成
mix giocci_bench.visualize
# セッションを指定してグラフ化
mix giocci_bench.visualize --session-dir giocci_bench_output/session_20260406-103137
# ワイルドカードでセッション指定(パターンに一致するセッション全てを一括処理)
mix giocci_bench.visualize --session-dir "giocci_bench_output/session_20260406-10*"
# report.html を生成してブラウザで開く
mix giocci_bench.visualize --open--out-dir-session_*を含む出力ディレクトリ(デフォルト: giocci_bench_output)--session-dir- 可視化対象のセッションディレクトリを明示指定(ワイルドカード:*,?,[...]に対応;マッチした全セッションを一括処理)--open- 生成後に既定ブラウザで HTML を開く(複数セッション時は全て開く)
生成先は常に各セッションディレクトリ配下の report.html です。
mix giocci_bench.visualize はセッション内の以下の CSV を自動検出して可視化します。
- 単体/シーケンス計測 CSV(
register_client.csv,save_module.csv,exec_func.csv,local_exec.csv,sequence.csv) - ping 計測 CSV(
ping.csv) - OS 情報 CSV(
*_os_info_free.csv,*_os_info_proc_stat.csv)*_os_info_proc_stat.csvは列ごとの生値ではなく、差分から算出したcpu_usage_pct(%)を表示- 計算式:
100 * (1 - idle_delta / total_delta)
レポートには折れ線グラフと、各列の基本統計(count, mean, median, min, max, stddev)が含まれます。
ヘッダには以下の情報が表示されます。
- セッションタイトル(
meta.jsonのsession_titleがある場合はそれを表示し、未指定時は固定タイトルGiocci Bench Visualizationを表示) - セッションディレクトリ名とレポート生成時刻
- 計測対象の MFArgs(
meta.jsonのcases)
# 同一タスクで実行した複数セッションを箱ひげ図で比較
mix giocci_bench.visualize.compare \
--session-dir giocci_bench_output/session_20260407-100000-sequence-nightly \
--session-dir giocci_bench_output/session_20260407-101000-sequence-baseline
# 出力先を指定(未指定時は giocci_bench_output/comparison_<日時>/report.html)
mix giocci_bench.visualize.compare \
--session-dir giocci_bench_output/session_20260407-100000-sequence-nightly \
--session-dir giocci_bench_output/session_20260407-101000-sequence-baseline \
--output tmp/compare_report.html
# ワイルドカード指定( "" 囲み必須)
mix giocci_bench.visualize.compare \
--session-dir "giocci_bench_output/session_20260407-10*-sequence-*"mix giocci_bench.visualize.compare の主な仕様:
--session-dirを2つ以上指定して比較(同一 Mix タスク種別のセッションのみ許可)*,?,[]のワイルドカード指定に対応(展開結果が比較対象)- セッションが異なる種別(single/sequence/local)ならエラー
--os-infoで取得した CSV(*_os_info_free.csv,*_os_info_proc_stat.csv)も箱ひげ図で比較- 指定したセッションのいずれかに os-info CSV が不足している場合は、不足セッション名を含めてエラー
*_os_info_proc_stat.csvは単一セッション可視化と同じく、差分から算出したcpu_usage_pct(%)を比較
- 凡例ラベルは各
meta.jsonのtitleを優先titleがない場合はA,B, ... で代用
- 比較レポートの既定出力先は
giocci_bench_output/comparison_<日時>/report.html - 各比較グラフについて CSV/SVG を
giocci_bench_output/comparison_<日時>/report/に出力
このリポジトリには Docker Compose でそのまま使える開発用コンテナを用意しています。
- ベースイメージ:
hexpm/elixir:1.19.5-erlang-28.3-ubuntu-noble-20251013 - 追加パッケージ:
chrony,iputils-ping,procps,git,build-essential - リポジトリ全体を
/workspaceに bind mount - コンテナ内の実行ユーザはホストの
uid/gidと名前をビルド時に合わせられる
LOCAL_USER="$(id -un)" \
LOCAL_UID="$(id -u)" \
LOCAL_GID="$(id -g)" \
docker compose build
LOCAL_USER/LOCAL_UID/LOCAL_GIDを省略した場合はdev:1000:1000で作成されます。
docker compose run で bash に入り、その中で mix コマンドを実行していく運用を想定しています。
LOCAL_USER="$(id -un)" \
LOCAL_UID="$(id -u)" \
LOCAL_GID="$(id -g)" \
docker compose run --rm giocci_bench以降はDockerコンテナ内での操作を示しています。
mix deps.get
mix compile
mix test
mix giocci_bench.single
mix giocci_bench.sequencechronyはイメージ内にインストールされますが、systemd を使わないためデーモンは自動起動しません。- Mix / Hex のホームディレクトリは Docker volume に保持されるため、コンテナを作り直してもキャッシュが残ります。
- ベンチマーク結果は bind mount されたワークスペース側に出力されます。