bin/gpu-bench times every GPU model (OCR mobile and server, the label image encoder, segmentation; optionally the CPU and the label text encoder) over a fixed 30-image corpus in tools/gpu_bench/corpus. Every corpus file is checked against corpus.txt's SHA-256 before anything runs; each model's files and models.config entry are fingerprinted; the GPU, driver, power limit, CUDA runtime, cuDNN and onnxruntime are recorded, and NVML samples memory, load, power and clocks during the timed passes. Results go to bin/gpu-bench-results as text files. The GPU Bench button opens a window that runs gpu-bench in its own process (no console) and lays results files from several machines side by side against a baseline, marking a different corpus or model.
92 lines
3.3 KiB
Plaintext
92 lines
3.3 KiB
Plaintext
// gpu-bench's results file: what gpu-bench writes, and what mcap's GPU Benchmark window reads to put
|
|
// several machines side by side. Plain text, one fact a line:
|
|
//
|
|
// gpu-bench 1
|
|
// machine: NAME a header line: key, colon, value
|
|
// ...
|
|
// model: ocr/ppocr-v5-mobile 1a2b... the fingerprint of the files a task's model is made of
|
|
// result: ocr/ppocr-v5-mobile CUDA ms_per_image=12.3 images_per_s=81.2 ...
|
|
//
|
|
// Two results are comparable only when their corpus line (the images) and each task's model line
|
|
// match; the window marks them where they do not.
|
|
|
|
BENCH_RESULTS_FORMAT :: "gpu-bench 1";
|
|
|
|
Bench_Pair :: struct { key, value: string; }
|
|
|
|
Bench_Row :: struct {
|
|
task: string; // ocr/ppocr-v5-mobile, labels/siglip2-base-patch16-384, segment/...
|
|
provider: string; // CUDA or CPU
|
|
fields: [..] Bench_Pair;
|
|
}
|
|
|
|
Bench_Report :: struct {
|
|
path: string;
|
|
header: [..] Bench_Pair; // in the file's order; model lines too, as "model ocr/x" -> hash
|
|
rows: [..] Bench_Row;
|
|
}
|
|
|
|
bench_header :: (report: *Bench_Report, key: string) -> string {
|
|
for report.header if it.key == key return it.value;
|
|
return "";
|
|
}
|
|
|
|
bench_field :: (row: *Bench_Row, key: string) -> string {
|
|
for row.fields if it.key == key return it.value;
|
|
return "";
|
|
}
|
|
|
|
bench_row :: (report: *Bench_Report, task: string, provider: string) -> *Bench_Row {
|
|
for * report.rows if it.task == task && it.provider == provider return it;
|
|
return null;
|
|
}
|
|
|
|
// A results file, or ok false and why; from the context allocator.
|
|
bench_parse :: (text: string, path := "") -> Bench_Report, ok: bool, error: string {
|
|
report: Bench_Report;
|
|
report.path = copy_string(path);
|
|
lines := split(text, "\n",, temp);
|
|
if !lines.count || trim(lines[0]) != BENCH_RESULTS_FORMAT return report, false, tprint("% is not a gpu-bench results file (no \"%\" on its first line).", path, BENCH_RESULTS_FORMAT);
|
|
for raw: lines {
|
|
if it_index == 0 continue;
|
|
line := trim(raw);
|
|
if !line.count || line[0] == #char "#" continue;
|
|
colon := find_index_from_left(line, #char ":");
|
|
if colon < 0 continue;
|
|
key := trim(slice(line, 0, colon));
|
|
value := trim(advance(line, colon + 1));
|
|
if key == "result" {
|
|
parts := split(value, " ",, temp);
|
|
if parts.count < 2 continue;
|
|
row := array_add(*report.rows);
|
|
row.* = .{task = copy_string(parts[0]), provider = copy_string(parts[1])};
|
|
for 2..parts.count - 1 {
|
|
part := parts[it];
|
|
equals := find_index_from_left(part, #char "=");
|
|
if equals < 0 continue;
|
|
array_add(*row.fields, .{copy_string(slice(part, 0, equals)), copy_string(advance(part, equals + 1))});
|
|
}
|
|
} else if key == "model" {
|
|
space := find_index_from_left(value, #char " ");
|
|
if space < 0 continue;
|
|
array_add(*report.header, .{tprint("model %", slice(value, 0, space),, context.allocator), copy_string(trim(advance(value, space + 1)))});
|
|
} else {
|
|
array_add(*report.header, .{copy_string(key), copy_string(value)});
|
|
}
|
|
}
|
|
return report, true, "";
|
|
}
|
|
|
|
bench_report_free :: (report: *Bench_Report) {
|
|
free(report.path);
|
|
for report.header { free(it.key); free(it.value); }
|
|
array_free(report.header);
|
|
for * report.rows {
|
|
free(it.task); free(it.provider);
|
|
for it.fields { free(it.key); free(it.value); }
|
|
array_free(it.fields);
|
|
}
|
|
array_free(report.rows);
|
|
report.* = .{};
|
|
}
|