# 帳票設計書 15-Benchmark レポート出力

## 概要

本ドキュメントは、mrubyのBenchmarkモジュールによるベンチマーク計測結果のフォーマット出力機能の設計仕様を記述する。Benchmark.bmメソッドにより、user/system/total/realの計測時間をテーブル形式で標準出力に表示する。

### 本帳票の処理概要

Benchmarkモジュールは、コードブロックの実行時間を計測し、フォーマットされたレポートを標準出力に出力する機能を提供する。Benchmark.bmメソッドでラベル付きのテーブル形式出力が可能であり、Benchmark.measureメソッドで個別の計測、Benchmark.realtimeメソッドで実時間のみの計測ができる。

**業務上の目的・背景**：パフォーマンスチューニングやコード最適化において、処理時間の定量的な計測は不可欠である。CRubyのBenchmarkライブラリに倣い、mrubyでも同等の計測・レポート機能を提供することで、組み込み環境でのパフォーマンス分析を可能にしている。mrubyではプロセス単位のCPU時間を取得できないため、user/systemは0.0固定でreal(実時間)が主要な計測値となる。

**帳票の利用シーン**：mrubyスクリプト内でBenchmark.bmメソッドを呼び出し、複数の処理を比較計測してレポートを出力する場面で使用される。パフォーマンス比較、回帰テスト、最適化効果の検証等に利用される。

**主要な出力内容**：
1. ヘッダー行：user/system/total/realの列ヘッダー
2. 計測結果行：ラベル + user/system/total/realの各時間値
3. オプション：オブジェクト数とメモリ使用量(memory: trueオプション時)

**帳票の出力タイミング**：Benchmark.bmまたはBenchmark::Report#reportメソッドが呼び出された時点で、各計測結果が順次標準出力に出力される。

**帳票の利用者**：mrubyスクリプトの開発者、パフォーマンスエンジニア

## 帳票種別

ベンチマークレポート(テキスト出力)

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| N/A | コンソール/ターミナル | N/A | Rubyスクリプト内でBenchmark.bmメソッドを呼び出し |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | テキスト(標準出力ストリーム) |
| 用紙サイズ | N/A |
| 向き | N/A |
| ファイル名 | N/A ($stdout への出力) |
| 出力方法 | $stdout.print / $stdout.puts による出力 |
| 文字コード | UTF-8 |

### PDF固有設定

N/A

### Excel固有設定

N/A

## 帳票レイアウト

### レイアウト概要

Benchmark.bmメソッドは、ヘッダー行に続いて各計測結果をテーブル形式で出力する。

```
┌─────────────────────────────────────────────────────────────┐
│ [ラベル幅スペース]      user     system      total        real│  ← ヘッダー行
├─────────────────────────────────────────────────────────────┤
│ [ラベル]          0.000000   0.000000   0.000000 (  0.001234)│  ← 計測結果行
│ [ラベル]          0.000000   0.000000   0.000000 (  0.002345)│
│ ...                                                         │
└─────────────────────────────────────────────────────────────┘
```

出力例:
```
                      user     system      total        real
array creation    0.000000   0.000000   0.000000 (  0.001234)
hash creation     0.000000   0.000000   0.000000 (  0.002345)
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | ラベルスペース | ラベル幅分のスペース(label_width > 0 の場合) | Benchmark.bm引数 | " " * label_width |
| 2 | 列ヘッダー | "      user     system      total        real" | 固定文字列 | 右寄せ固定幅 |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | ラベル | 計測対象の名前 | Report#report引数 | 左寄せ、ラベル幅に右パディング | label_width文字 |
| 2 | user時間 | ユーザーCPU時間 | Tms#utime | "%10.6f"形式 | 10文字 |
| 3 | system時間 | システムCPU時間 | Tms#stime | "%10.6f"形式 | 10文字 |
| 4 | total時間 | 合計時間(utime+stime+cutime+cstime) | Tms#total | "%10.6f"形式 | 10文字 |
| 5 | real時間 | 実経過時間 | Tms#real | "(%10.6f)"形式 | 12文字 |

### フッター部

N/A (フッターなし)

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| $stdout非nil | $stdoutがnilの場合は出力をスキップ | Yes |
| mruby-benchmark gem | mruby-benchmark gemがビルドに含まれていること | Yes |
| mruby-time gem | 依存gem(Time.nowを使用) | Yes |
| mruby-sprintf gem | 依存gem(フォーマット文字列出力に使用) | Yes |
| mruby-io gem | 依存gem($stdout.print/$stdout.putsに使用) | Yes |

### ソート順

N/A (Report#reportの呼び出し順に出力)

### 改ページ条件

N/A

## データベース参照仕様

### 参照テーブル一覧

N/A

### テーブル別参照項目詳細

N/A

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| real時間 | end_time - start_time (Time.now) | 小数点以下6桁表示 | 実経過時間(秒) |
| total時間 | utime + stime + cutime + cstime | 小数点以下6桁表示 | mrubyでは全て0.0のため常に0.0 |
| user時間 | 0.0 (固定) | 小数点以下6桁表示 | mrubyではプロセスCPU時間を取得不可 |
| system時間 | 0.0 (固定) | 小数点以下6桁表示 | mrubyではプロセスCPU時間を取得不可 |
| objects_allocated | end_objects - start_objects | N/A | memory:trueオプション時のみ計測、ObjectSpace使用 |
| memory_allocated | objects_allocated * 40 | N/A | 1オブジェクトあたり平均40バイトの推定値 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[Benchmark.bm 呼び出し] --> B[Report オブジェクト生成]
    B --> C{label_width > 0?}
    C -->|Yes| D[ラベル幅分のスペース出力]
    C -->|No| E[ヘッダー出力]
    D --> E
    E --> F[ブロック yield report]
    F --> G[report.report ラベル 呼び出し]
    G --> H[Benchmark.measure でブロック実行]
    H --> I[Tms オブジェクト生成]
    I --> J[ラベル + Tms#to_s 出力]
    J --> K{次の report 呼び出しあり?}
    K -->|Yes| G
    K -->|No| L[Report オブジェクト返却]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| $stdout nil | $stdoutがnilに設定されている場合 | (出力なし - スキップ) | $stdoutを適切なIOオブジェクトに設定 |
| mruby-time未導入 | Time.nowが利用できない場合 | (NameError/NoMethodError) | mruby-time gemをビルドに含める |
| ObjectSpace未導入 | memory:true指定時にObjectSpaceが利用できない場合 | (計測スキップ - nil返却) | mruby-objectspace gemをビルドに含める |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 計測項目数に依存(通常数件〜数十件) |
| 目標出力時間 | 計測対象ブロックの実行時間 + 微小なオーバーヘッド |
| 同時出力数上限 | N/A (シングルスレッド実行) |

## セキュリティ考慮事項

- ベンチマーク結果はタイミング情報を含むため、暗号処理等のセキュリティクリティカルな計測には使用すべきでない(タイミング攻撃のリスク)
- $stdoutへの出力であるため、出力内容にアクセス制御は適用されない

## 備考

- mrubyではプロセス単位のCPU時間を取得できないため、user/system/cutime/cstimeは全て0.0が固定値として設定される。実質的にはreal(実経過時間)のみが有意な計測値となる
- memory:trueオプションでObjectSpaceを使用したオブジェクト数計測が可能。メモリ使用量はオブジェクト数 * 40バイトの推定値
- 依存gem: mruby-time, mruby-objectspace, mruby-sprintf, mruby-io
- Tms#formatメソッドでカスタムフォーマットが可能(%u=user, %s=system, %t=total, %r=real, %o=objects, %m=memory, %n=label)

---

## コードリーディングガイド

本帳票を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

Benchmark::Tmsクラスの属性と計算を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | benchmark.rb | `mrbgems/mruby-benchmark/mrblib/benchmark.rb` | Tmsクラス定義(行2-37) - utime/stime/cutime/cstime/real/objects/memory属性 |
| 1-2 | benchmark.rb | `mrbgems/mruby-benchmark/mrblib/benchmark.rb` | Tms#to_s(行22-24) - フォーマット文字列 "%10.6f %10.6f %10.6f (%10.6f)\n" |
| 1-3 | benchmark.rb | `mrbgems/mruby-benchmark/mrblib/benchmark.rb` | Tms#format(行26-36) - カスタムフォーマット(%u/%s/%t/%r/%o/%m/%n) |

**読解のコツ**: Tmsクラスは計測結果のデータホルダー。to_sメソッドのフォーマット文字列がそのままレポートの列レイアウトを決定する。totalメソッド(行18-20)はutime+stime+cutime+cstimeの合計。

#### Step 2: エントリーポイントを理解する

Benchmark.bmメソッドの処理フローを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | benchmark.rb | `mrbgems/mruby-benchmark/mrblib/benchmark.rb` | Benchmark.bm(行116-130) - ヘッダー出力とReportへのyield |

**主要処理フロー**:
1. **行117**: Report.new(label_width)でReportオブジェクト生成
2. **行120-125**: $stdoutがnilでなければヘッダー行を出力(label_width分のスペース + 列ヘッダー)
3. **行127**: yield reportでユーザブロックにReportオブジェクトを渡す
4. **行129**: reportオブジェクトを返却

#### Step 3: 計測と出力の処理を理解する

Report#reportメソッドとBenchmark.measureメソッドの連携を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | benchmark.rb | `mrbgems/mruby-benchmark/mrblib/benchmark.rb` | Report#report(行46-64) - 計測実行と結果出力 |
| 3-2 | benchmark.rb | `mrbgems/mruby-benchmark/mrblib/benchmark.rb` | Benchmark.measure(行72-105) - 時間計測ロジック |
| 3-3 | benchmark.rb | `mrbgems/mruby-benchmark/mrblib/benchmark.rb` | Benchmark.realtime(行108-113) - 実時間計測 |

**主要処理フロー**:
- **行47 (Report#report)**: Benchmark.measureでブロック実行時間を計測
- **行49-50 (Report#report)**: Tmsオブジェクトにラベルを設定して再生成
- **行52-54 (Report#report)**: ラベルをlabel_width分にパディング
- **行57-59 (Report#report)**: $stdout.printでラベルとTms#to_sを出力
- **行73-74 (Benchmark.measure)**: Time.nowで開始時刻を記録
- **行84 (Benchmark.measure)**: yieldでユーザブロックを実行
- **行86-87 (Benchmark.measure)**: Time.nowで終了時刻を記録し差分を計算
- **行104 (Benchmark.measure)**: Tms.new(0.0, 0.0, 0.0, 0.0, real)で結果オブジェクト生成

### プログラム呼び出し階層図

```
Benchmark.bm(label_width) (Ruby側)
    |
    +-- Report.new(label_width) (行117)
    |
    +-- $stdout.puts ヘッダー出力 (行124)
    |
    +-- yield report (行127)
           |
           +-- report.report(label) { ブロック } (行46)
                  |
                  +-- Benchmark.measure { yield } (行47)
                  |      |
                  |      +-- Time.now (開始) (行73)
                  |      +-- yield (ブロック実行) (行84)
                  |      +-- Time.now (終了) (行86)
                  |      +-- Tms.new(0.0, 0.0, 0.0, 0.0, real) (行104)
                  |
                  +-- Tms.new(... label ...) (行49-50)
                  +-- $stdout.print label_str (行58)
                  +-- $stdout.print tms.to_s (行59)
                         |
                         +-- Tms#to_s (行22-24)
                                "%10.6f %10.6f %10.6f (%10.6f)\n"
```

### データフロー図

```
[入力]                    [処理]                          [出力]

ユーザブロック     Benchmark.measure()
(コードブロック) ──> (Time.now差分計測) ──> Tms生成 ──> $stdout
                          |                    |
                          +── [memory:true]     +── to_s フォーマット
                          |   ObjectSpace       |   "%10.6f ..."
                          |   count_objects     |
                          +── Tms.new()         +── ヘッダー行
                                                    "user system ..."
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| benchmark.rb | `mrbgems/mruby-benchmark/mrblib/benchmark.rb` | ソース | Benchmarkモジュール全体の実装(Tms/Report/bm/measure/realtime) |
| mrbgem.rake | `mrbgems/mruby-benchmark/mrbgem.rake` | 設定 | gem仕様定義、依存関係(mruby-time/objectspace/sprintf/io) |
| benchmark.rb | `mrbgems/mruby-benchmark/test/benchmark.rb` | テスト | Benchmarkモジュールのテストケース |
