Skip to content
Open
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
161 changes: 152 additions & 9 deletions TraceLens/TraceIndex/sqlite_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,42 @@
utc_now,
)

# Promoted from unified_perf_summary.csv into typed columns so catalogs can
# filter without json_extract on raw_row_json.
UNIFIED_ROOFLINE_COLUMNS = (
("gpu_kernel_pct", "REAL"),
("pct_roofline_max", "REAL"),
("pct_roofline_mean", "REAL"),
("pct_roofline_median", "REAL"),
("pct_roofline_min", "REAL"),
("pct_roofline_std", "REAL"),
("roofline_bound", "TEXT"),
("roofline_time_us", "REAL"),
)

_ROOFLINE_TIME_KEYS = (
"Roofline Time (\u00b5s)_first",
"Roofline Time (us)_first",
"Roofline Time (\u00b5s)",
"Roofline Time (us)",
)


def extract_unified_roofline(row: Dict[str, Any]) -> Dict[str, Any]:
"""Pull roofline / GPU-share fields from a CSV row or parsed raw_row_json."""
return {
"gpu_kernel_pct": as_float(
first_value(row, ["Percentage (%)", "gpu_kernel_pct", "percent"])
),
"pct_roofline_max": as_float(first_value(row, ["Pct Roofline_max"])),
"pct_roofline_mean": as_float(first_value(row, ["Pct Roofline_mean"])),
"pct_roofline_median": as_float(first_value(row, ["Pct Roofline_median"])),
"pct_roofline_min": as_float(first_value(row, ["Pct Roofline_min"])),
"pct_roofline_std": as_float(first_value(row, ["Pct Roofline_std"])),
"roofline_bound": as_text(first_value(row, ["Roofline Bound"])),
"roofline_time_us": as_float(first_value(row, _ROOFLINE_TIME_KEYS)),
}


def is_read_only_sql(sql: str) -> bool:
stripped = sql.strip().lower()
Expand Down Expand Up @@ -124,6 +160,13 @@ def init_schema(self) -> None:
has_perf_model INTEGER,
overlap_pct REAL,
gpu_kernel_pct REAL,
pct_roofline_max REAL,
pct_roofline_mean REAL,
pct_roofline_median REAL,
pct_roofline_min REAL,
pct_roofline_std REAL,
roofline_bound TEXT,
roofline_time_us REAL,
perf_params_json TEXT,
kernel_details_json TEXT,
raw_row_json TEXT
Expand Down Expand Up @@ -216,8 +259,102 @@ def init_schema(self) -> None:
tokenize='unicode61'
);
""")
added = self._ensure_unified_roofline_columns()
if added:
self._backfill_roofline_from_raw()
self.conn.commit()

def _ensure_unified_roofline_columns(self) -> List[str]:
existing = {
row["name"]
for row in self.conn.execute("PRAGMA table_info(unified_perf_rows)")
}
added: List[str] = []
alter_sql = {
"gpu_kernel_pct": (
"ALTER TABLE unified_perf_rows ADD COLUMN gpu_kernel_pct REAL"
),
"pct_roofline_max": (
"ALTER TABLE unified_perf_rows ADD COLUMN pct_roofline_max REAL"
),
"pct_roofline_mean": (
"ALTER TABLE unified_perf_rows ADD COLUMN pct_roofline_mean REAL"
),
"pct_roofline_median": (
"ALTER TABLE unified_perf_rows ADD COLUMN pct_roofline_median REAL"
),
"pct_roofline_min": (
"ALTER TABLE unified_perf_rows ADD COLUMN pct_roofline_min REAL"
),
"pct_roofline_std": (
"ALTER TABLE unified_perf_rows ADD COLUMN pct_roofline_std REAL"
),
"roofline_bound": (
"ALTER TABLE unified_perf_rows ADD COLUMN roofline_bound TEXT"
),
"roofline_time_us": (
"ALTER TABLE unified_perf_rows ADD COLUMN roofline_time_us REAL"
),
}
for name, _decl in UNIFIED_ROOFLINE_COLUMNS:
if name in existing:
continue
self.conn.execute(alter_sql[name])
added.append(name)
self.conn.execute(
"CREATE INDEX IF NOT EXISTS idx_trace_index_unified_roofline_bound "
"ON unified_perf_rows(roofline_bound)"
)
self.conn.execute(
"CREATE INDEX IF NOT EXISTS idx_trace_index_unified_pct_roofline_mean "
"ON unified_perf_rows(pct_roofline_mean)"
)
return added

def _backfill_roofline_from_raw(self) -> None:
rows = self.conn.execute(
"SELECT id, raw_row_json FROM unified_perf_rows WHERE raw_row_json IS NOT NULL"
).fetchall()
updates = []
for row in rows:
try:
payload = json.loads(row["raw_row_json"])
except (TypeError, json.JSONDecodeError):
continue
if not isinstance(payload, dict):
continue
fields = extract_unified_roofline(payload)
updates.append(
(
fields["gpu_kernel_pct"],
fields["pct_roofline_max"],
fields["pct_roofline_mean"],
fields["pct_roofline_median"],
fields["pct_roofline_min"],
fields["pct_roofline_std"],
fields["roofline_bound"],
fields["roofline_time_us"],
row["id"],
)
)
if not updates:
return
self.conn.executemany(
"""
UPDATE unified_perf_rows SET
gpu_kernel_pct = COALESCE(gpu_kernel_pct, ?),
pct_roofline_max = COALESCE(pct_roofline_max, ?),
pct_roofline_mean = COALESCE(pct_roofline_mean, ?),
pct_roofline_median = COALESCE(pct_roofline_median, ?),
pct_roofline_min = COALESCE(pct_roofline_min, ?),
pct_roofline_std = COALESCE(pct_roofline_std, ?),
roofline_bound = COALESCE(roofline_bound, ?),
roofline_time_us = COALESCE(roofline_time_us, ?)
WHERE id = ?
""",
updates,
)

def upsert_trace(self, trace: TraceRecord) -> int:
now = utc_now()
by_path = self.conn.execute(
Expand Down Expand Up @@ -427,6 +564,7 @@ def _import_unified_rows(
kernel_details = parse_repr(
first_value(row, ["kernel_details_summary", "trunc_kernel_details"])
)
roofline = extract_unified_roofline(row)
cursor = self.conn.execute(
"""
INSERT INTO unified_perf_rows(
Expand All @@ -435,10 +573,13 @@ def _import_unified_rows(
kernel_time_std_us, kernel_time_min_us, kernel_time_max_us,
op_duration_us, tflops_mean, tflops_median, tbs_mean, tbs_median,
gflops, data_moved_mb, flops_per_byte, compute_spec,
has_perf_model, overlap_pct, gpu_kernel_pct, perf_params_json,
kernel_details_json, raw_row_json
has_perf_model, overlap_pct, gpu_kernel_pct,
pct_roofline_max, pct_roofline_mean, pct_roofline_median,
pct_roofline_min, pct_roofline_std, roofline_bound,
roofline_time_us, perf_params_json, kernel_details_json,
raw_row_json
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
trace_id,
Expand Down Expand Up @@ -516,12 +657,14 @@ def _import_unified_rows(
as_text(first_value(row, ["Compute Spec", "compute_spec"])),
as_bool_int(first_value(row, ["has_perf_model", "Has Perf Model"])),
as_float(first_value(row, ["overlap_pct", "Overlap (%)"])),
as_float(
first_value(
row,
["Percentage (%)", "gpu_kernel_pct", "percent"],
)
),
roofline["gpu_kernel_pct"],
roofline["pct_roofline_max"],
roofline["pct_roofline_mean"],
roofline["pct_roofline_median"],
roofline["pct_roofline_min"],
roofline["pct_roofline_std"],
roofline["roofline_bound"],
roofline["roofline_time_us"],
to_json(params),
to_json(kernel_details),
to_json(dict(row)),
Expand Down
17 changes: 17 additions & 0 deletions docs/how-to/trace-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ Query GEMM / SDPA / convolution shapes from the satellite tables, or with
`json_extract` on the parsed JSON columns. For example
`SELECT "M", "N", "K" FROM gemm_perf` or
`SELECT json_extract(perf_params_json, '$.M') FROM unified_perf_rows`.
Roofline stats are typed columns (`pct_roofline_mean`, `roofline_bound`,
`roofline_time_us`), not JSON.

## Example queries

Expand Down Expand Up @@ -206,6 +208,21 @@ Channels equal groups (true depthwise) at 3×3 and 5×5 with widths 1536 / 3072
depthwise-conv workload, that's the file to open — found without reopening any
trace.

### Which GEMMs are compute-bound with low roofline utilization?

```sql
SELECT t.name, u.name AS op, u.pct_roofline_mean, u.roofline_bound
FROM unified_perf_rows u
JOIN traces t ON t.id = u.trace_id
WHERE u.op_category = 'GEMM'
AND u.roofline_bound = 'COMPUTE_BOUND'
AND u.pct_roofline_mean < 40
ORDER BY u.pct_roofline_mean;
```

Roofline bound and utilization are typed columns on `unified_perf_rows`, so this
filter does not need `json_extract` on `raw_row_json`.

### What are the longest attention sequences in the catalog?

```sql
Expand Down
7 changes: 7 additions & 0 deletions docs/reference/trace-index-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ The following table lists the columns in `unified_perf_rows`.
| `has_perf_model` | INTEGER | `1` when the row has a perf model, else `0`. |
| `overlap_pct` | REAL | Overlap percentage from the report. |
| `gpu_kernel_pct` | REAL | Operation share from the report's `Percentage (%)` column. |
| `pct_roofline_max` | REAL | Maximum `Pct Roofline` across instances of this op. |
| `pct_roofline_mean` | REAL | Mean `Pct Roofline` across instances of this op. |
| `pct_roofline_median` | REAL | Median `Pct Roofline` across instances of this op. |
| `pct_roofline_min` | REAL | Minimum `Pct Roofline` across instances of this op. |
| `pct_roofline_std` | REAL | Standard deviation of `Pct Roofline`. |
| `roofline_bound` | TEXT | `COMPUTE_BOUND` or `MEMORY_BOUND` from the report's `Roofline Bound` column. |
| `roofline_time_us` | REAL | Theoretical roofline time in microseconds (`Roofline Time (µs)_first`). |
| `perf_params_json` | TEXT | Parsed `perf_params` as JSON. |
| `kernel_details_json` | TEXT | Parsed `kernel_details_summary` as JSON. |
| `raw_row_json` | TEXT | Full source CSV row as JSON. |
Expand Down
Loading
Loading