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
62 changes: 62 additions & 0 deletions apps/resolution-time/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Procedural TypeGPU function generator for benchmarking resolution time

## Adding a new instruction

Each instruction is a `tgpu.comptime` that returns a `tgpu.fn(() => void)`. There are two kinds: **leaf** (no function calls) and **recursive** (branches into other instructions).

### Leaf instruction

```ts
const myLeafFn = tgpu.comptime(() => {
return tgpu.fn(() => {
'use gpu';
// ...
popDepth(); // REQUIRED — always call at the end
}).$name('myLeafFn');
});
```

### Recursive instruction

Use `tgpu.unroll` over `arrayForUnroll(BRANCHING)` and call `instructions[choice()]()()` to branch into other instructions. The `choice()` function handles depth tracking and picks a leaf when at max depth.

```ts
const myRecursiveFn = tgpu.comptime(() => {
return tgpu.fn(() => {
'use gpu';
// ...
for (const _i of tgpu.unroll(arrayForUnroll(BRANCHING))) {
instructions[choice()]()();
}
popDepth(); // REQUIRED — always call at the end, after the unroll
}).$name('myRecursiveFn');
});
```

## Generating Mermaid charts

`generateChart.ts` reads benchmark result JSON files and outputs a Mermaid xychart comparing up to 3 datasets.

### Usage

```sh
node generateChart.ts \
--input "PR:pr-branch/results-max-depth.json" \
--input "main:main-branch/results-max-depth.json" \
--title "Resolution Time vs Max Depth" \
--xAxisTitle "max depth" \
--yAxisTitle "time (ms)" \
--output chart.md
```

### Arguments

| Argument | Required | Description |
|---|---|---|
| `--input <label>:<path>` | yes (1-3) | `label` appears in the legend, `path` points to a benchmark results JSON file generated by `procedural.ts` |
| `--title <text>` | yes | chart title |
| `--xAxisTitle <text>` | no | x-axis title |
| `--yAxisTitle <text>` | no | y-axis title |
| `--output <path>` | no | write markdown to a file instead of stdout |

Each `--input` dataset is plotted as a separate line on the same chart. Colors are assigned in order: red, blue, green.
1 change: 1 addition & 0 deletions apps/resolution-time/bunfig.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
preload = ["./preload.ts"]
112 changes: 112 additions & 0 deletions apps/resolution-time/generateChart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { readFileSync, writeFileSync } from 'node:fs';
import type { BenchmarkResult } from './procedural.ts';

interface Dataset {
label: string;
data: BenchmarkResult[];
}

const args = process.argv.slice(2);

const inputs: Dataset[] = [];
let title = '';
let output = '';
let xAxisTitle = '';
let yAxisTitle = '';

// parsing arguments
for (let i = 0; i < args.length; i++) {
if (args[i] === '--input') {
// oxlint-disable-next-line typescript/no-non-null-assertion
const [label, filepath] = args[++i]!.split(':', 2);
inputs.push({
label: label as string,
data: JSON.parse(readFileSync(filepath as string, 'utf8')),
});
} else if (args[i] === '--title') {
if (title !== '') {
throw new Error('Ambiguous title');
}
// oxlint-disable-next-line typescript/no-non-null-assertion
title = args[++i]!;
} else if (args[i] === '--output') {
if (output !== '') {
throw new Error('Ambiguous output');
}
// oxlint-disable-next-line typescript/no-non-null-assertion
output = args[++i]!;
} else if (args[i] === '--xAxisTitle') {
if (xAxisTitle !== '') {
throw new Error('Ambiguous xAxisTitle');
}
// oxlint-disable-next-line typescript/no-non-null-assertion
xAxisTitle = args[++i]!;
} else if (args[i] === '--yAxisTitle') {
if (yAxisTitle !== '') {
throw new Error('Ambiguous yAxisTitle');
}
// oxlint-disable-next-line typescript/no-non-null-assertion
yAxisTitle = args[++i]!;
}
}

if (inputs.length === 0) {
throw new Error('No input path provided');
}

if (inputs.length > 3) {
throw new Error('Inputs are limited to 3');
}

if (title === '') {
throw new Error('No title provided');
}

function median(values: number[]): number {
// oxlint-disable-next-line eslint-plugin-unicorn(no-array-sort)
const sorted = values.sort((a, b) => a - b);
const mid = Math.floor(sorted.length / 2);
// oxlint-disable-next-line typescript/no-non-null-assertion
return sorted.length % 2 ? sorted[mid]! : (sorted[mid - 1]! + sorted[mid]!) / 2;
}

// oxlint-disable-next-line eslint-plugin-unicorn(no-array-sort)
const allDepths = [...new Set(inputs.flatMap((ds) => ds.data.map((d) => d.maxDepth)))].sort(
(a, b) => a - b,
);

// mermaid chart
const lines: string[] = [];

const symbols = ['\u{1f534}', '\u{1f535}', '\u{1f7e2}'];
const legend = inputs.map((ds, i) => `${symbols[i]} ${ds.label}`).join(' | ');

lines.push('```mermaid');
lines.push(`---
config:
themeVariables:
xyChart:
plotColorPalette: "#E63946, #3B82F6, #059669"
---`);
lines.push('xychart');
lines.push(` title "${title} (${legend})"`);
lines.push(` x-axis "${xAxisTitle ? xAxisTitle : ' '}" [${allDepths.join(', ')}]`);
lines.push(` y-axis "${yAxisTitle ? yAxisTitle : ' '}"`);

for (const ds of inputs) {
const medians = allDepths.map((depth) => {
const times = ds.data.filter((d) => d.maxDepth === depth).map((d) => d.timeMs);
return times.length ? median(times).toFixed(2) : '0';
});
lines.push(` line [${medians.join(', ')}]`);
}

lines.push('```');

const md = lines.join('\n');

if (output) {
writeFileSync(output, md);
} else {
console.log(md);
}
17 changes: 17 additions & 0 deletions apps/resolution-time/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "resolution-time",
"version": "0.0.0",
"private": true,
"description": "Resolution time benchmark app for TypeGPU",
"type": "module",
"scripts": {
"resolution-time": "bun procedural.ts"
},
"dependencies": {
"typegpu": "workspace:*"
},
"devDependencies": {
"bun": "latest",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's safest to pin a specific version range

Suggested change
"bun": "latest",
"bun": "^1.3.10",

"unplugin-typegpu": "workspace:*"
}
}
4 changes: 4 additions & 0 deletions apps/resolution-time/preload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { plugin } from 'bun';
import typegpu from 'unplugin-typegpu/bun';

void plugin(typegpu({}));
Loading
Loading