Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
PROPERTY VALUE "${CMAKE_BINARY_DIR}/install")
endif()

# Add rpath to link with dependencies shared libraries
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH "\${ORIGIN}/../lib")

option(PROT_ENABLE_WERROR "Enable -Werror option (CI)" OFF)
option(PROT_BUILD_BENCHMARKS
"Enable benchmarks build (requires riscv gnu toolchain)" OFF)
Expand Down
13 changes: 13 additions & 0 deletions cmake/dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,16 @@ CPMAddPackage(
OPTIONS "BUILD_TESTING OFF"
DOWNLOAD_ONLY True
)

# perf-cpp
CPMAddPackage(
NAME perf-cpp
GITHUB_REPOSITORY jmuehlig/perf-cpp
GIT_TAG v0.12.6
# Don't add EXCLUDE_FROM_ALL, as perf-cpp will be used as shared library
SYSTEM True
OPTIONS
"BUILD_LIB_SHARED ON"
"CMAKE_INSTALL_LIBRARY_DIR ${CMAKE_INSTALL_PREFIX}/lib"
)
target_include_directories(perf-cpp PUBLIC $<BUILD_INTERFACE:${perf-cpp_SOURCE_DIR}/include>)
4 changes: 2 additions & 2 deletions src/jit/base/base.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void JitEngine::step(CPUState &cpu) {
}
curAddr += isa::kWordSize;
}
} else if (bbIt->second.num_exec >= kExecThreshold) [[likely]] {
} else if (bbIt->second.num_exec >= m_translationThreshold) [[likely]] {
auto code = translate(bbIt->second);
m_tbCache.insert(pc, code);
if (code != nullptr) [[likely]] {
Expand All @@ -60,7 +60,7 @@ void JitEngine::interpret(CPUState &cpu, BBInfo &info) {

auto JitEngine::getBBInfo(isa::Addr pc) const -> const BBInfo * {
if (const auto found = m_cacheBB.find(pc); found != m_cacheBB.end()) {
if (found->second.num_exec >= kExecThreshold) {
if (found->second.num_exec >= m_translationThreshold) {
return &found->second;
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/jit/base/include/prot/jit/base.hh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "prot/interpreter.hh"

#include <cstddef>
#include <functional>
#include <map>
#include <unordered_map>
Expand All @@ -11,10 +12,11 @@
namespace prot::engine {
using JitFunction = void (*)(CPUState &);
class JitEngine : public Interpreter {
static constexpr std::size_t kExecThreshold = 10;

public:
void step(CPUState &cpu) override;
void setTranslationThreshold(size_t threshold) {
m_translationThreshold = threshold;
}

protected:
struct TbCache {
Expand Down Expand Up @@ -63,6 +65,7 @@ private:
private:
[[nodiscard]] virtual JitFunction translate(const BBInfo &info) = 0;

size_t m_translationThreshold = 0;
TbCache m_tbCache;
std::unordered_map<isa::Addr, BBInfo> m_cacheBB;
};
Expand Down
114 changes: 114 additions & 0 deletions tools/bench/CLAUDE_Plot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# CLAUDE_Plot.md — Benchmark Metrics Plotter

## Project overview

`Plot.py` — a single Python script for visu alising benchmark performance
data collected across multiple series (e.g. JIT compilers, build configs).
Produces SVG charts. Two regimes selectable via `--regime`.

## Usage

```bash
# Bars: compare absolute metric values across series
python3 Plot.py \
--regime bars \
--data results/llvm results/asmjit results/gcc \
--metrics cycles ipc l1i-cache-misses \
--out plots/

# Graphs: show how metrics scale with a base metric (workload scaling)
python3 Plot.py \
--regime graphs \
--data results/llvm results/asmjit results/gcc \
--base-metric instructions \
--metrics cycles ipc \
--out plots/
```

## CLI arguments

| Argument | Required | Description |
|---|---|---|
| `--regime` | yes | `bars` or `graphs` |
| `--data` | yes | One or more series directories; dir name = legend label |
| `--metrics` | yes | CSV column names to plot |
| `--base-metric` | graphs only | Column used as X axis (workload proxy) |
| `--out` | no | Output directory for SVGs (default: `./plots`) |
| `--logx` | no | Logarithmic X axis (graphs regime only) |
| `--logy` | no | Logarithmic Y axis (both regimes) |

## Data layouts

### `bars` regime

```
<series-dir>/
<benchmark>.csv ← rows = independent repeated runs
```

- Benchmarks matched across series by filename (without extension)
- One SVG per metric, named `<metric>.svg`

### `graphs` regime

```
<series-dir>/
<benchmark>/
<workload_size>.csv ← rows = independent repeated runs at that size
```

- Benchmarks matched across series by sub-directory name
- One SVG per dependent metric, named `<metric>_vs_<base-metric>.svg`

## CSV format

Plain CSV with a header row. Column names are metric names. Each data row is
one independent run. Example:

```
instructions,cycles,branches,l1i-cache-misses
100,50,10,1
110,50,11,2
```

## How values are computed

- **Bar height / line Y point** — geometric mean of all rows in a CSV file
- **Error bars / shaded band** — ±1 standard deviation across rows
- **Line X point** (`graphs` only) — geometric mean of `--base-metric` in that CSV

Geomean is used rather than arithmetic mean because it is more appropriate for
ratios and hardware counters that span orders of magnitude. Non-positive and
non-finite values are excluded before computing geomean (required since
`log(x)` is undefined for `x ≤ 0`).

## Axis formatting

On linear axes, matplotlib's `ScalarFormatter` is used with `useMathText=True`
and `powerlimits=(-3, 3)`, which produces an automatic `×10^N` offset label
when values are large or small. The axis label shows only the bare metric name.

On log axes (`--logx` / `--logy`), matplotlib's default `LogFormatter` is used,
showing ticks in `10^x` notation. `nonpositive="clip"` is passed to
`set_xscale`/`set_yscale` so that bars starting from 0 and any negative
error-bar lower bounds are silently clipped to a small positive value rather
than crashing or producing invisible elements.

## Dependencies

```bash
pip install numpy pandas matplotlib
```

## Known issues / notes

- In `graphs_plot`, the emptiness check on `all_benches` is now an `assert`
rather than a soft print-and-return, reflecting that an empty `series_data`
should be caught upstream in `run_graphs` and never reach this point.
- `--base-metric` is silently ignored when `--regime bars` is used.
- Series with no valid CSV files produce a warning and are skipped rather than
causing a hard error.
- In `graphs` regime, CSV files missing the dependent metric column produce a
warning and are skipped (the base metric missing is also warned).
- Missing benchmark/series combinations in `bars` regime show an "N/A"
annotation on the bar rather than crashing.
Loading
Loading