Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Machine-Readable Output

With --output-format=default|json|pretty-json (env: GUNGRAUN_OUTPUT_FORMAT) you can change the terminal output format to the machine-readable json format. The json schemas fully describing the json output are stored here:

Iai-Callgrind/Gungraun versionSchema version
>=0.9.0,<0.11.0summary.v1.schema.json
>=0.11.0,<0.14.0summary.v2.schema.json
>=0.14.0,<0.15.0summary.v3.schema.json
>=0.15.0,<0.15.2summary.v4.schema.json
>=0.15.2,<0.16.0summary.v5.schema.json
>=0.16.0,<0.20.0summary.v6.schema.json
>=0.20.0summary.v7.schema.json

Each line of json output (if not pretty-json) is a summary of a single benchmark, and you may want to combine all benchmarks in an array. You can do so for example with jq

cargo bench -- --output-format=json | jq -s

which transforms {...}\n{...} into [{...},{...}].

Instead of, or in addition to changing the terminal output, it’s possible to save a summary file for each benchmark with --save-summary=json|pretty-json (env: GUNGRAUN_SAVE_SUMMARY).

Parsing summary.json

Each summary.json contains the result of one benchmark. The file is stored in the benchmark’s output directory, so consumers can process individual benchmarks without first splitting the terminal’s JSON stream.

The examples in this section use schema version 7. In this format, tool data is stored in profiles, with aggregate metrics under paths such as .data.total.metrics.Ir. Metric values are plain JSON numbers in values.new and, when a baseline exists, values.old. A comparison is stored in change. Its diff_pct and factor values are strings because they can contain values such as "inf".

Optional fields are omitted when they have no value. The exception is baselines, a fixed two-element array whose missing entries are null.

Complete schema v7 summary.json example
{
  "baselines": [null, null],
  "benchmark_exe": "target/release/deps/test_lib_bench_intro-0123456789abcdef",
  "benchmark_file": "crates/gungraun-tests/benches/test_lib_bench/intro/test_lib_bench_intro.rs",
  "duration_ns": 200553102,
  "function_name": "bench_fibonacci_with_config",
  "group": "fibonacci",
  "kind": "LibraryBenchmark",
  "module_path": "test_lib_bench_intro::fibonacci::bench_fibonacci_with_config",
  "output_dir": "target/gungraun/gungraun-tests/test_lib_bench_intro/fibonacci/bench_fibonacci_with_config",
  "package_dir": "crates/gungraun-tests",
  "profiles": [
    {
      "data": {
        "parts": [
          {
            "metrics": {
              "D1MissRate": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": {
                  "new": 0.33444816053511706,
                  "old": 0.33444816053511706
                }
              },
              "D1mr": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 1, "old": 1 }
              },
              "D1mw": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 0, "old": 0 }
              },
              "DLmr": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 0, "old": 0 }
              },
              "DLmw": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 0, "old": 0 }
              },
              "Dr": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 161, "old": 161 }
              },
              "Dw": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 138, "old": 138 }
              },
              "EstimatedCycles": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 1092, "old": 1092 }
              },
              "I1MissRate": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": {
                  "new": 0.43668122270742354,
                  "old": 0.43668122270742354
                }
              },
              "I1mr": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 3, "old": 3 }
              },
              "ILmr": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 3, "old": 3 }
              },
              "Ir": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 687, "old": 687 }
              },
              "L1HitRate": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 99.59432048681542, "old": 99.59432048681542 }
              },
              "L1hits": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 982, "old": 982 }
              },
              "LLHitRate": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": {
                  "new": 0.10141987829614604,
                  "old": 0.10141987829614604
                }
              },
              "LLMissRate": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": {
                  "new": 0.3042596348884381,
                  "old": 0.3042596348884381
                }
              },
              "LLdMissRate": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 0.0, "old": 0.0 }
              },
              "LLhits": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 1, "old": 1 }
              },
              "LLiMissRate": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": {
                  "new": 0.43668122270742354,
                  "old": 0.43668122270742354
                }
              },
              "RamHitRate": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": {
                  "new": 0.3042596348884381,
                  "old": 0.3042596348884381
                }
              },
              "RamHits": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 3, "old": 3 }
              },
              "TotalRW": {
                "change": { "diff_pct": "0", "factor": "1" },
                "values": { "new": 986, "old": 986 }
              }
            },
            "tool_run": {
              "new": {
                "command": "/home/user/my-project/target/release/deps/test_lib_bench_intro-0123456789abcdef --gungraun-run d:d:000000 00001 00001 00000",
                "part": 1,
                "pid": 2565576,
                "thread": 1
              },
              "old": {
                "command": "/home/user/my-project/target/release/deps/test_lib_bench_intro-0123456789abcdef --gungraun-run d:d:000000 00001 00001 00000",
                "part": 1,
                "pid": 2557960,
                "thread": 1
              }
            }
          }
        ],
        "total": {
          "metrics": {
            "D1MissRate": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": {
                "new": 0.33444816053511706,
                "old": 0.33444816053511706
              }
            },
            "D1mr": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 1, "old": 1 }
            },
            "D1mw": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 0, "old": 0 }
            },
            "DLmr": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 0, "old": 0 }
            },
            "DLmw": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 0, "old": 0 }
            },
            "Dr": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 161, "old": 161 }
            },
            "Dw": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 138, "old": 138 }
            },
            "EstimatedCycles": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 1092, "old": 1092 }
            },
            "I1MissRate": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": {
                "new": 0.43668122270742354,
                "old": 0.43668122270742354
              }
            },
            "I1mr": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 3, "old": 3 }
            },
            "ILmr": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 3, "old": 3 }
            },
            "Ir": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 687, "old": 687 }
            },
            "L1HitRate": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 99.59432048681542, "old": 99.59432048681542 }
            },
            "L1hits": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 982, "old": 982 }
            },
            "LLHitRate": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": {
                "new": 0.10141987829614604,
                "old": 0.10141987829614604
              }
            },
            "LLMissRate": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 0.3042596348884381, "old": 0.3042596348884381 }
            },
            "LLdMissRate": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 0.0, "old": 0.0 }
            },
            "LLhits": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 1, "old": 1 }
            },
            "LLiMissRate": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": {
                "new": 0.43668122270742354,
                "old": 0.43668122270742354
              }
            },
            "RamHitRate": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 0.3042596348884381, "old": 0.3042596348884381 }
            },
            "RamHits": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 3, "old": 3 }
            },
            "TotalRW": {
              "change": { "diff_pct": "0", "factor": "1" },
              "values": { "new": 986, "old": 986 }
            }
          },
          "regressions": []
        }
      },
      "duration_ns": 200542320,
      "process_ns": 200490228,
      "started_at": "2026-09-25T16:02:04.121681251Z",
      "tool": "Callgrind"
    }
  ],
  "project_root": "/home/user/my-project",
  "started_at": "2026-09-25T16:02:04.121680298Z",
  "version": "7"
}

gungraun-summary

The gungraun-summary crate provides typed Rust data structures for reading summary files. Its major version tracks the latest supported schema version, so use version 7 when consuming schema v7 files:

[dependencies]
gungraun-summary = "7"

The version modules are self-contained. If the version of a file is already known, parse it directly with the v7 module:

extern crate gungraun_summary;
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let summary = gungraun_summary::v7::parse(Path::new("summary.json"))?;
    println!("{}", summary.function_name);
    Ok(())
}

For input whose version is not known in advance, util::parse first reads the top-level version field and then selects the matching representation:

extern crate gungraun_summary;
use std::path::Path;

use gungraun_summary::util::{SummaryByVersion, parse};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let summary = match parse(Path::new("summary.json"))? {
        SummaryByVersion::V7(summary) => summary,
        _ => return Err("expected a schema v7 summary".into()),
    };
    println!("{}", summary.function_name);
    Ok(())
}

Examples with jq

The following commands read the complete example summary shown above. Set a variable to the path of any schema v7 summary.json to use them with your own results:

summary=summary.example.json

Extract the new Callgrind instruction-read count:

jq '.profiles[] | select(.tool == "Callgrind") | .data.total.metrics.Ir.values.new' "$summary"
687

Convert the Callgrind metrics map to a list containing each metric’s values and percentage change. The final slice keeps this example’s output short; remove | .[:2] to return all metrics.

jq '[
  .profiles[]
  | select(.tool == "Callgrind")
  | .data.total.metrics
  | to_entries[]
  | {
      metric: .key,
      new: .value.values.new,
      old: .value.values.old,
      diff_pct: (.value.change.diff_pct? | tonumber)
    }
] | .[:2]' "$summary"
[
    {
        "metric": "D1MissRate",
        "new": 0.33444816053511706,
        "old": 0.33444816053511706,
        "diff_pct": 0
    },
    {
        "metric": "D1mr",
        "new": 1,
        "old": 1,
        "diff_pct": 0
    }
]

Filter metrics by the absolute percentage change. This example uses a threshold of zero so it produces output for the unchanged sample; use a value such as 5 to find changes of at least five percent. The explicit infinity check is needed before tonumber because diff_pct can be "inf" or "-inf".

jq --argjson threshold 0 '
  first(
    .profiles[]
    | select(.tool == "Callgrind")
    | .data.total.metrics
    | to_entries[]
    | select(
        .value.change.diff_pct? as $diff
        | $diff != "inf"
          and $diff != "-inf"
          and (($diff | tonumber | fabs) >= $threshold)
      )
    | {
        metric: .key,
        new: .value.values.new,
        old: .value.values.old,
        diff_pct: .value.change.diff_pct
      }
  )
' "$summary"
{
    "metric": "D1MissRate",
    "new": 0.33444816053511706,
    "old": 0.33444816053511706,
    "diff_pct": "0"
}

Extract the fields that identify a benchmark:

jq '{ group, function_name, module_path, id }' "$summary"
{
    "group": "fibonacci",
    "function_name": "bench_fibonacci_with_config",
    "module_path": "test_lib_bench_intro::fibonacci::bench_fibonacci_with_config",
    "id": null
}