# 機能設計書 76-計時

## 概要

本ドキュメントは、Julia言語における計時機能の設計を記述する。`@time`、`@timed`、`@elapsed`、`@timev`、`@showtime`、`@allocated`、`@allocations` マクロによるコード実行時間・メモリ使用量の計測を提供する。

### 本機能の処理概要

計時機能は、Juliaコードの実行性能を計測するための一連のマクロを提供する。実行時間、メモリ割り当て量、GC時間、コンパイル時間、再コンパイル時間、ロック競合数など、多角的な性能メトリクスを取得できる。

**業務上の目的・背景**：性能最適化はJuliaの主要な設計目標の一つであり、コードの実行性能を正確に計測する能力はJuliaプログラマにとって不可欠である。`@time`マクロはJuliaで最もよく使われるデバッグツールの一つであり、メモリ割り当てのボトルネック発見やコンパイル時間の把握に広く利用される。

**機能の利用シーン**：関数の実行時間計測、メモリ割り当ての最適化（ゼロアロケーション目標）、GCの影響評価、コンパイルオーバーヘッドの分離、ベンチマーク前の予備評価、ロック競合の検出などで使用される。

**主要な処理内容**：
1. `@time expr` - 実行時間・アロケーション・GC時間・コンパイル時間・ロック競合を表示
2. `@time "description" expr` - 説明文付きの計時
3. `@showtime expr` - 式の文字列表現付きの計時
4. `@timev expr` - 詳細な計時（個別カウンタ表示）
5. `@elapsed expr` - 経過時間のみを数値で返却
6. `@timed expr` - 全計測値をNamedTupleで返却
7. `@allocated expr` - バイト単位のアロケーション量を返却
8. `@allocations expr` - アロケーション回数を返却
9. `@lock_conflicts expr` - ロック競合数を返却
10. `@time_imports expr` - インポート時間の計測
11. `@trace_compile expr` - コンパイルトレース

**関連システム・外部連携**：GCサブシステム（gc_num/gc_total_bytes）、JITコンパイラ（cumulative_compile_time_ns）、スレッドシステム（lock_profiling）と連携する。

**権限による制御**：特別な権限制御はない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | Juliaプロンプト（julia>） | 主機能 | REPLからの@time等のマクロ使用 |

## 機能種別

性能計測処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| expr | Expr | Yes | 計測対象の式 | 任意のJulia式 |
| msg | String/Nothing | No | 説明文（@time, @timev） | 文字列またはnothing |

### 入力データソース

- Juliaソースコード内のマクロ呼び出し
- REPLからの対話的使用

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| value | Any | @timeの場合、式の評価結果 |
| time | Float64 | @timedの経過時間（秒） |
| bytes | Int64 | 割り当てバイト数 |
| gctime | Float64 | GC時間（秒） |
| gcstats | GC_Diff | GC統計の差分 |
| lock_conflicts | Int | ロック競合数 |
| compile_time | Float64 | コンパイル時間（秒） |
| recompile_time | Float64 | 再コンパイル時間（秒） |
| elapsed_seconds | Float64 | @elapsedの経過時間 |
| allocated_bytes | Int64 | @allocatedのバイト数 |
| allocation_count | Int | @allocationsのアロケーション回数 |

### 出力先

- `@time` / `@timev` / `@showtime` : stdout にフォーマット済みテキスト出力
- `@elapsed` / `@allocated` / `@allocations` / `@lock_conflicts` : 数値として返却
- `@timed` : NamedTupleとして返却

## 処理フロー

### 処理シーケンス

```
1. 計測前準備
   └─ Experimental.@force_compile で事前コンパイル
   └─ lock_profilingの有効化（@timed）
   └─ GCカウンタの初期値取得（gc_num()）
   └─ 開始時刻記録（time_ns()）
   └─ コンパイル計測有効化（cumulative_compile_timing(true)）
2. 式の実行
   └─ ユーザー式を評価
3. 計測後処理（@__tryfinallyで保証）
   └─ 終了時刻記録（time_ns()）
   └─ コンパイル計測無効化
   └─ lock_profilingの無効化
4. 差分計算
   └─ GC_Diff(gc_num(), stats) でGC統計差分を算出
5. 結果出力
   └─ time_print / timev_print でフォーマット出力
```

### フローチャート

```mermaid
flowchart TD
    A["@time / @timed 呼び出し"] --> B["Experimental.@force_compile"]
    B --> C["GCカウンタ初期値取得 gc_num()"]
    C --> D["開始時刻記録 time_ns()"]
    D --> E["コンパイル計測有効化"]
    E --> F["式の実行"]
    F --> G["終了時刻記録 time_ns()"]
    G --> H["コンパイル計測無効化"]
    H --> I["GC_Diff算出"]
    I --> J{"出力方式"}
    J -->|"@time"| K["time_print → stdout"]
    J -->|"@timed"| L["NamedTuple返却"]
    J -->|"@elapsed"| M["Float64返却"]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-76-01 | 事前コンパイル | @force_compileで計測前にマクロ内部コードをコンパイル | 全計測マクロ |
| BR-76-02 | @__tryfinally | 計測終了処理はtry-finally相当で保証される | @timed |
| BR-76-03 | GC差分計算 | gc_total_bytes = allocd + deferred_alloc + total_allocd | GC_Diff算出時 |
| BR-76-04 | 時間単位 | 内部はナノ秒、表示は秒（小数点6桁） | time_print |
| BR-76-05 | アロケーション計測 | 関数呼び出し形式の場合、呼び出しオーバーヘッドを除外する最適化あり | @allocated, @allocations |
| BR-76-06 | ロック競合計測 | Threads.lock_profilingの有効化/無効化で計測 | @timed, @lock_conflicts |

### 計算ロジック

- `gc_alloc_count(diff) = diff.malloc + diff.realloc + diff.poolalloc + diff.bigalloc`
- `gc_total_bytes(gc_num) = gc_num.allocd + gc_num.deferred_alloc + gc_num.total_allocd`
- GC時間のパーセンテージ: `100 * gctime / elapsedtime`
- コンパイル時間のパーセンテージ: `100 * compile_time / elapsedtime`

## データベース操作仕様

### 操作別データベース影響一覧

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | - | 計測対象式が例外をスロー | @__tryfinallyで計測終了処理は保証される |

### リトライ仕様

リトライ不要

## トランザクション仕様

該当なし

## パフォーマンス要件

- time_ns()はナノ秒精度のタイムスタンプを返す軽量操作
- gc_num()はccallによるGCカウンタ取得で低オーバーヘッド
- @force_compileにより計測マクロ自体のコンパイルコストを計測外に排除
- 計測オーバーヘッドは通常数マイクロ秒程度

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

特になし

## 備考

- より正確なベンチマークにはBenchmarkTools.jlの`@btime`が推奨される
- `@time @eval ...`で最初のコンパイル時間を含めた計測が可能
- Julia 1.8で説明文オプション、再コンパイル時間分離が追加
- Julia 1.11でロック競合の表示が追加

---

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

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

### 推奨読解順序

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

GC統計データの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | timing.jl | `base/timing.jl` | GC_Num構造体（4-75行目）: GCカウンタの全フィールド。allocd, total_allocd, total_time等の意味 |
| 1-2 | timing.jl | `base/timing.jl` | GC_Diff構造体（80-90行目）: GC統計の差分を表現。allocd, malloc, poolalloc等 |

**読解のコツ**: GC_NumはC構造体（src/gc-interface.h）とメモリレイアウトが同期されている。GC_Diffはnew-oldで差分を計算する。

#### Step 2: 計測マクロの実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | timing.jl | `base/timing.jl` | @timedマクロ（730-758行目）: 計測の完全な実装。gc_num, time_ns, cumulative_compile_time_ns, lock_profiling を使用 |
| 2-2 | timing.jl | `base/timing.jl` | @timeマクロ（393-406行目）: @timedの結果をtime_printでフォーマット出力 |
| 2-3 | timing.jl | `base/timing.jl` | @elapsedマクロ（499-506行目）: time_ns()の差分のみ |
| 2-4 | timing.jl | `base/timing.jl` | @allocated / @allocationsマクロ（620-643行目）: _gen_allocation_measurer関数で生成 |

**主要処理フロー**:
1. **730-758行目**: `@timed` - 最も包括的な計測。gc_num取得→time_ns→compile_timing有効化→式実行→(@__tryfinally)→終了時刻→GC_Diff→NamedTuple返却
2. **393-406行目**: `@time` - @timedを呼び出し、time_printでstdoutに出力
3. **554-590行目**: `_gen_allocation_measurer` - 関数呼び出し形式の最適化。callの場合は直接Base.allocatedを前置

#### Step 3: 出力フォーマットを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | timing.jl | `base/timing.jl` | time_print（252-303行目）: 実行時間・アロケーション・GC%・コンパイル%のフォーマット |
| 3-2 | timing.jl | `base/timing.jl` | timev_print（305-322行目）: 詳細カウンタの表示 |
| 3-3 | timing.jl | `base/timing.jl` | format_bytes（241-250行目）: バイト数の人間可読フォーマット |

**主要処理フロー**:
- **252-303行目**: `time_print` - Ryu.writefixedで浮動小数点フォーマット。アロケーション/GC%/コンパイル%を括弧内に表示
- **241-250行目**: `format_bytes` - KiB/MiB/GiB単位への自動変換

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

```
@time expr
    │
    └─ @timed expr
           │
           ├─ Experimental.@force_compile
           ├─ Threads.lock_profiling(true)
           ├─ gc_num()                    // GCカウンタ初期値
           │      └─ ccall(:jl_gc_num)
           ├─ time_ns()                   // 開始時刻
           ├─ cumulative_compile_timing(true)
           ├─ cumulative_compile_time_ns()
           ├─ [式の実行]
           ├─ time_ns() - elapsedtime     // 経過時間
           ├─ cumulative_compile_timing(false)
           ├─ GC_Diff(gc_num(), stats)    // GC差分
           └─ NamedTuple構築

    └─ time_print(stdout, ...)
           │
           ├─ Ryu.writefixed()            // 時間フォーマット
           ├─ format_bytes()              // バイトフォーマット
           └─ prettyprint_getunits()      // 単位選択
```

### データフロー図

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

gc_num() ──────▶ GC_Num(before)
                         │
expr ──────────▶ [式の実行]
                         │
gc_num() ──────▶ GC_Num(after) ──▶ GC_Diff ──▶ allocd, total_time, ...

time_ns() ─────▶ before
time_ns() ─────▶ after ──────────▶ elapsed ──▶ Float64 (秒)

compile_time_ns() ▶ before
compile_time_ns() ▶ after ────────▶ diff ─────▶ compile_time, recompile_time

                   time_print() ──────────────▶ stdout (フォーマット済みテキスト)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| timing.jl | `base/timing.jl` | ソース | 全計時マクロ・ユーティリティ関数の実装 |
| gcutils.jl | `base/gcutils.jl` | ソース | GC.gc/GC.enable等のGC制御（GC_Numの取得先） |
| gc-interface.h | `src/gc-interface.h` | ヘッダ（C） | GC_Num C構造体定義 |
| lock.jl | `base/lock.jl` | ソース | lock_profiling, LOCK_CONFLICT_COUNT |
| Ryu/ | `base/ryu/` | ソース | 浮動小数点の文字列フォーマット |
