TFLOPS and tokens/sec harnesses for the load-bearing kernels. Each is a
single .c file and reports median over a fixed number of iterations
after warmup. The default benches link the static SDK archive; selected
variants also link the shared runtime when that is the path being measured.
./build/bench/bench_gemm # GEMM TFLOPS sweep
./build/bench/bench_gemm_shared # GEMM through shared libtensorcore
./build/bench/bench_attention # FlashAttention TFLOPS
./build/bench/bench_inference_7b # 7B Q4_0 decode latencyAll three follow the same pattern: 3 warmup calls, 10-20 measured calls, report median TFLOPS / tok-s with the backend that served it. See ../docs/benchmarks.md for current measured numbers.
GEMM throughput sweep across square shapes 256-4096 and dtypes fp16 / fp32. Prints one line per (dtype, shape) cell:
f16 M=4096 N=4096 K=4096 simdgroup_matrix median=7.69 ms 17.88 TFLOPS
The reported backend should be simdgroup_matrix on every M-series
chip; if it's mps or accelerate_cpu, you found a kernel-coverage
gap or a path that fell back unexpectedly. See
../docs/family_gating.md.
The default sweep is sized for GPU runs. For bounded smoke tests or the portable CPU backend, override the sweep:
TC_BENCH_DTYPES=f32 TC_BENCH_SIZES=256,512 TC_BENCH_WARMUP=1 TC_BENCH_ITERS=3 \
./build/bench/bench_gemmSupported TC_BENCH_DTYPES entries are f16, f32, and bf16. GPU-scale
results print as TFLOPS; tiny or CPU-scale results print as GFLOPS.
Try the experimental large tile:
TC_USE_128_TILE=1 ./build/bench/bench_gemm(v0.1 regresses on M2; v0.2 retunes.)
For Linux AVX2 OpenMP work, use the shared-runtime target. The shared library owns OpenMP internally, while the static archive remains dependency-light for SDK consumers:
TC_USE_AVX2_GEMM=1 TC_AVX2_THREADS=64 \
TC_BENCH_DTYPES=f32 TC_BENCH_SIZES=2048,4096 \
./build/bench/bench_gemm_sharedFlashAttention forward TFLOPS at D=64 fp16, causal=true, across common transformer shapes (B=1, H ∈ {8,16,32}, S ∈ {512, 1024, 2048, 4096}).
B=1 H=32 S=4096 D=64 causal=1 simdgroup_matrix median=19.4 ms 7.07 TFLOPS
D=128 isn't in the sweep yet; the v0.1 D=128 kernel is correctness- validated but tile-tuning is a v0.2 item — see ../docs/attention.md.
The synthetic 7B Q4_0 decode harness. Allocates random Q4_0 weights at the 7B Llama shape (32 layers × hidden=4096 × mlp=11008) and times the per-token GEMV work, excluding attention/RoPE/RMSnorm — pure GEMV throughput.
=== tensorcore 7B Q4_0 decode latency bench ===
hidden=4096 heads=32 head_dim=128 mlp_dim=11008 layers=32
Q4_0 weight footprint per 7B model: 3.39 GB
Results (20 tokens x 5 repeats, 32 layers, Q4_0 GEMVs only):
median tok/s : 186.3
median weight bw: 632.2 GB/s
Reference: llama.cpp on M2 Ultra reports ~55-65 tok/s. The current async-batched harness is ~3-3.5× ahead of that on pure GEMV throughput. End-to-end inference (attention + softmax + RoPE + RMSnorm on top) is a v0.2 integration target.
Switch to the v1 Q4_0 kernel for comparison:
TC_Q4_USE_V1=1 ./build/bench/bench_inference_7bSee ../docs/quantized.md for the v1 → v2 kernel design diff.
Pattern (from bench_gemm.c):
#include "tensorcore/tensorcore.h"
#include <stdio.h>
#include <time.h>
static double now_seconds(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (double)ts.tv_sec + (double)ts.tv_nsec * 1e-9;
}
int main(void) {
tc_context* ctx; tc_init(&ctx);
/* 1. Allocate inputs once */
/* 2. Warm up: 3 calls (lets pipeline cache + buffer pool settle) */
/* 3. Time N calls; record times[i] */
/* 4. qsort and report times[N/2] (median) */
/* 5. Report TFLOPS = (2*M*N*K / time) * 1e-12 */
/* 6. Print tc_backend_name(tc_last_backend()) per cell */
tc_shutdown(ctx);
return 0;
}Register in bench/CMakeLists.txt:
add_executable(bench_mything bench_mything.c)
target_link_libraries(bench_mything PRIVATE tensorcore)
target_include_directories(bench_mything PRIVATE ${CMAKE_SOURCE_DIR}/include)| Bench | Counts | Excludes |
|---|---|---|
bench_gemm |
2 * M * N * K FLOPs per call |
warmup, dispatch overhead amortized in median |
bench_attention |
2 * B * H * S² * D FLOPs per call (counts both halves of causal mask in the score-FLOPs total — the reported TFLOPS is "effective" user-perceived) |
same |
bench_inference_7b |
weight bytes read per token (memory-bound metric) | attention, softmax, RoPE, RMSnorm |
These are per-call numbers; nothing is amortized across calls except warmup. For end-to-end inference, expect lower throughput once attention
- KV-cache + sampling are in the loop.
- ../docs/benchmarks.md — current measured numbers with chip + shape + backend.
- ../docs/observability.md § Backend tracing — how to interpret the backend field in bench output.