williamhtan/kernelsight
KernelSight v4 Per-timestep workload labels for GPU execution traces. KernelSight pairs every GPU workload trace with a dense, per-timestep workload labeling. Each snapshot is a [24, 512] counter image — 24 hardware-counter channels sampled across 512 equal-width time bins — paired with per-bin labels drawn from a two-level hierarchy of 12 coarse (L1) and 73 fine (L2) workload classes. The goal is to label what a kernel is doing at each instant (matmul, attention, reduction… See the full description on the dataset page: https://huggingface.co/datasets/williamhtan/kernelsight.
KernelSight v4
Per-timestep workload labels for GPU execution traces.
KernelSight pairs every GPU workload trace with a dense, per-timestep workload labeling. Each snapshot is a [24, 512] counter image — 24 hardware-counter channels sampled across 512 equal-width time bins — paired with per-bin labels drawn from a two-level hierarchy of 12 coarse (L1) and 73 fine (L2) workload classes. The goal is to label what a kernel is doing at each instant (matmul, attention, reduction, memory movement, …) rather than the single coarse bottleneck label a profiler assigns per launch.
All counters are collected on a single NVIDIA H100 80GB HBM3 (Hopper, sm_90a). No data augmentation is applied: each snapshot is the trace as measured, and class imbalance is handled at training time rather than by resampling.
Quick start
The dataset ships as raw NumPy .npz files in a fixed directory layout, indexed by JSON split files. It is not loadable through datasets.load_dataset(); download the folder and read the .npz files directly with NumPy.
from huggingface_hub import snapshot_download
import numpy as np, json, os
root = snapshot_download(repo_id="williamhtan/kernelsight", repo_type="dataset")
# Load a split (paths inside are relative to `root`)
split = json.load(open(os.path.join(root, "splits/train.json")))
rec = split["traces"][0]
# Input tensor
t = np.load(os.path.join(root, rec["path"]), allow_pickle=True)
X = t["data"] # (24, 512) float32
# Labels live alongside the input
lpath = rec["path"].replace("/input/", "/labels/").replace("tensor_input.npz", "labels.npz")
l = np.load(os.path.join(root, lpath), allow_pickle=True)
y_l1 = l["workload_l1"] # (512,) int32, -1 where unlabeled
y_l2 = l["workload_l2"] # (512,) int32
mask = l["mask_labeled"] # (512,) uint8
y_mh = l["workload_l1_multihot"] # (512, 12) uint8, multi-label overlap trackA reference PyTorch Dataset is bundled at tools/sass_dataloader_stub.py, and the label vocabularies live in tools/workload_taxonomy.py (single source of truth). Full schema documentation is in `data_info.md`.
Directory layout
kernelsight_dataset_v4/
├── README.md # this card
├── MANIFEST_v4.md # release notes / per-class counts
├── data_info.md # full on-disk schema reference
├── splits/ # 7 split JSONs (see Splits)
├── tools/
│ ├── workload_taxonomy.py # 12 L1 + 73 L2 + 8 flags + 5 spatial (source of truth)
│ └── sass_dataloader_stub.py
└── kernels/<motif>/_out/<variant>/
├── input/tensor_input.npz # [24, 512] profiler heatmap + metadata
├── labels/labels.npz # per-bin + per-segment L1/L2 labels, vocabs
└── fingerprint/fingerprint.npz # 32-D instruction-mix fingerprintEach <variant> is one snapshot (e.g. parameter-swept geometry like cutlass_gemm/_out/m8192_n1024_k4096_.../). Split JSON path fields are relative to the dataset root and point at .../input/tensor_input.npz.
Input tensor — tensor_input.npz
data—[24, 512]float32: 24 counter channels × 512 time bins.time_edges_ns—[513]int64: bin-boundary timestamps (bins are equal-width per trace, ~0.5 ms for fast matmul up to ~30 ms for long scatter; the window is clipped to the kernel-active span).counter_names—[24]: channel names.kernels,kernel_names,kernel_function_index— per-launch identity metadata.
Channels divide into five semantic groups. Each channel is divided by a fixed physical-scale divisor (pipe/throughput ÷100, warp-stall ÷64, coalescing ÷8) so values land in ~[0, 1] while preserving magnitude differences (no per-channel min/max rescale).
Labels — labels.npz
Per-bin arrays (length T = 512):
Per-segment arrays (length S, varies by motif): segment_starts, segment_ends, segment_label_l1, segment_label_l2, segment_kernel_names, segment_predecessor_l1/l2, segment_position, attribute_flags [S,8].
Vocabularies (carried in every labels.npz): vocab_l1[12], vocab_l2[73], attribute_flag_names[8], spatial_state_vocab[5], l2_parent_l1[73].
The single-label fields are always a subset of the multi-hot tracks. On the sequential corpus the multi-hot is effectively one-hot; genuine overlap comes from 472 cutlass_ws_overlap snapshots whose producer (TMA load → memory_movement) and consumer (WGMMA → matmul) phases co-occur, derived from device %globaltimer markers (independent of the 24 counter channels — no label leakage).
Label taxonomy
L1 (12): matmul, conv, activation, normalization, softmax, pooling, reduction, attention, loss, elementwise, memory_movement, other.
L2 (73), nested under L1 parents:
Attribute flags (8, multi-label per segment): sparse, tma, cluster, masked, persistent, vectorized_store, atomic_accum, ldgsts.
Spatial-state vocab (5, exposed for the model side): uniform, wavefront_transition, tail_effect, load_imbalanced, hotspot.
Corpus composition
Per-L1 distribution (snapshots containing each class / labeled segments): matmul 849 / 3,257 · activation 147 / 11,654 · reduction 125 / 7,155 · conv 98 / 6,418 · attention 92 / 295 · elementwise 86 / 2,887 · normalization 79 / 6,546 · pooling 62 / 2,019 · memory_movement 48 / 48 · loss 42 / 4,860 · softmax 28 /
- The corpus is heavily imbalanced (matmul dominates bin count via long CUTLASS traces), which motivates a class-balanced objective and macro-F1.
Splits
Each split JSON is {split, seed, n, traces, notes}; every traces[i] records path, motif, n_kernels, n_unique_kernels, T, l1_labels, l2_labels, dominant_l1, dominant_l2.
Standard disjoint partition (L2-stratified, trace-level leak-free):
This measures within-kernel generalization: most test traces share kernel identity with training and differ in geometry/precision/sweep parameters.
Overlapping analysis tags (views over the same corpus, not a partition):
Collection methodology
Workloads come from three sources — hand-written CUDA microbenchmarks isolating canonical GPU behaviors, the KernelBench Level-1 / Level-2 problem suite, and CUTLASS Hopper examples (the single largest contributor, ~61% of the corpus) spanning six warp-specialized datapaths (TF32, FP8, 2:4-sparse, grouped GEMM, FlashAttention-3, and WS-GEMM with device markers).
Each workload is profiled by three complementary collectors and fused onto one time grid:
- NVBit — SASS-level dynamic binary instrumentation: per-PC instruction mix and coalescing statistics.
- CUPTI Range Profiler — replays each kernel for a 19-metric warp-stall taxonomy (stall reasons, pipe utilizations, occupancy).
- Nsight Systems — samples system throughput at ~10 kHz alongside the CUDA/NVTX timeline; the only natively time-resolved source, so it defines the time grid.
Labels come from NVTX markers + kernel boundaries, with kernel-name + SASS pattern matching resolving the L2 class. The full collector fork and per-motif run.sh reproduction harness are part of the KernelSight project (not bundled in this dataset distribution, which ships the rendered tensors, labels, and splits).
Changes from v3.1
- Dropped
megakernel(1 PoC snapshot) andtiled_gemm_poc(590 hand-written PoC snapshots). - Added three CUTLASS Hopper datapaths: FP8 (ex54), 2:4 sparse (ex62), grouped (ex57).
- Selective KernelBench expansion (activation, normalization, pooling, reduction, elementwise) and geometry sweeps over microbenchmarks and CUTLASS GEMM/FMHA.
- Corpus 262 → 1,444 snapshots; overlap ground truth 29 → 472 snapshots.
- CI: 26,996 assertions passed, 0 failed.
See `MANIFEST_v4.md` for full release notes.
Limitations
- Counters are from a single H100 (
sm_90a); cross-architecture transfer is out of scope. - Overlap timing is coarse: device-marker spans resolve producer/consumer envelopes (≈ whole launch), so overlap is annotated at launch granularity.
- All 24 channels carry real signal, but many rows are legitimately zero where the hardware is inactive for a given motif.
- The
spatial_statevocab is exposed for the model side; per-bin spatial-state derivation is not provided.
License & provenance
Released under Apache-2.0. Derived workloads retain their upstream licenses:
- KernelBench problems — MIT (Scaling Intelligence Lab, Stanford University).
- CUTLASS examples — BSD-3-Clause (NVIDIA Corporation).
The profiler tooling builds on the Intra-Kernel Profiler (NVBit / CUPTI / Nsight Systems). This release contains only derived, aggregated counter tensors and labels — no third-party source code.
Citation
@misc{tan2026kernelsight,
title = {KernelSight: Per-Timestep Workload Labeling of GPU Execution Traces},
author = {Tan, William},
year = {2026},
note = {CS231N project, Stanford University},
howpublished = {\url{https://huggingface.co/datasets/williamhtan/kernelsight}}
}