Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
63227e3
initial commit
cieplypolar Mar 1, 2026
79f8787
readme by claude
cieplypolar Mar 1, 2026
1aed4d2
Merge branch 'main' into feat/proc-gen-func
cieplypolar Mar 3, 2026
b542027
refactor
cieplypolar Mar 3, 2026
b124f2b
python + ci
cieplypolar Mar 3, 2026
e40fff6
generating charts with mermaid
cieplypolar Mar 3, 2026
21a5291
initial commit
cieplypolar Mar 3, 2026
bc342fa
Merge branch 'main' into feat/proc-gen-func
cieplypolar Mar 3, 2026
4ef79d2
oxfmt
cieplypolar Mar 3, 2026
2c6c86b
Merge branch 'feat/proc-gen-func' of github.com:software-mansion/Type…
cieplypolar Mar 3, 2026
780872b
review fixes
cieplypolar Mar 3, 2026
0e34944
review changes
cieplypolar Mar 3, 2026
861f182
oxfmt
cieplypolar Mar 3, 2026
49e7c52
Merge branch 'feat/proc-gen-func' into feat/resolution-time-ci
cieplypolar Mar 3, 2026
f949f6d
oxfmt
cieplypolar Mar 3, 2026
e5954d5
readme update
cieplypolar Mar 3, 2026
42bfa9f
heavy leaf
cieplypolar Mar 4, 2026
8129935
readme update
cieplypolar Mar 3, 2026
18c3673
heavy leaf
cieplypolar Mar 4, 2026
71e1215
Merge branch 'feat/proc-gen-func' into feat/resolution-time-ci
cieplypolar Mar 4, 2026
f14d6ae
Merge branch 'main' into feat/resolution-time-ci
cieplypolar Mar 27, 2026
eef307e
Merge branch 'main' into feat/resolution-time-ci
cieplypolar Mar 27, 2026
2786f9a
Merge branch 'main' into feat/resolution-time-ci
cieplypolar Mar 27, 2026
de0fdc2
github action and small fixes to procedural.ts
cieplypolar Mar 27, 2026
2ba77a3
oxfmt
cieplypolar Mar 27, 2026
fc29929
vitest left
cieplypolar Mar 27, 2026
3564492
fix procedural.ts - wrong splice
cieplypolar Mar 27, 2026
6edf7ab
Merge branch 'main' into feat/resolution-time-ci
cieplypolar Apr 2, 2026
1528003
std.range + ci cleanup
cieplypolar Apr 2, 2026
977432f
Merge branch 'main' into feat/resolution-time-ci
cieplypolar Apr 2, 2026
1ecac0e
new seed per sample
cieplypolar Apr 2, 2026
b893cde
Merge branch 'feat/resolution-time-ci' of github.com:software-mansion…
cieplypolar Apr 2, 2026
c7267eb
readme update
cieplypolar Apr 2, 2026
bee7952
Merge branch 'main' into feat/resolution-time-ci
cieplypolar Apr 2, 2026
44d8a49
Merge branch 'main' into feat/resolution-time-ci
cieplypolar Apr 3, 2026
a79e167
Merge branch 'main' into feat/resolution-time-ci
cieplypolar Apr 3, 2026
d5046e8
Merge branch 'main' into feat/resolution-time-ci
cieplypolar Apr 7, 2026
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
176 changes: 176 additions & 0 deletions .github/workflows/resolution-time.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
name: Resolution time benchmark

on:
pull_request:

jobs:
resolution-time:
if: github.event.pull_request.head.repo.full_name == github.repository
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10.27.0
run_install: false

- name: Checkout PR branch
uses: actions/checkout@v4
with:
path: pr-branch

- name: Checkout target branch
uses: actions/checkout@v4
with:
path: target-branch
ref: ${{ github.base_ref }}

- name: Checkout latest release
uses: actions/checkout@v4
with:
path: release-branch
ref: release

- name: Copy resolution-time app to release checkout
run: |
if [ ! -d release-branch/apps/resolution-time ]; then
cp -r target-branch/apps/resolution-time release-branch/apps/resolution-time
echo "Copied resolution-time app from target branch to release checkout"
else
echo "resolution-time app already exists in release checkout"
fi
Comment on lines +37 to +44
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So, we only copy the folder if it's missing, right? Will this step be removed after next release?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

yes, I included this if statement in case I forget about it 👻


- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 24.x
cache: 'pnpm'
cache-dependency-path: |
pr-branch/pnpm-lock.yaml
target-branch/pnpm-lock.yaml
release-branch/pnpm-lock.yaml

- name: Install dependencies (PR branch)
working-directory: pr-branch
run: pnpm install --frozen-lockfile && cd apps/resolution-time/node_modules/bun && node install.js

- name: Install dependencies (target branch)
working-directory: target-branch
run: pnpm install --frozen-lockfile && cd apps/resolution-time/node_modules/bun && node install.js

- name: Install dependencies (release branch)
working-directory: release-branch
run: pnpm install --no-frozen-lockfile && cd apps/resolution-time/node_modules/bun && node install.js

- name: Run benchmark (PR branch)
working-directory: pr-branch
run: pnpm --filter resolution-time resolution-time

- name: Run benchmark (target branch)
working-directory: target-branch
run: pnpm --filter resolution-time resolution-time

- name: Run benchmark (release branch)
working-directory: release-branch
run: pnpm --filter resolution-time resolution-time

- name: Generate chart (random)
run: |
node pr-branch/apps/resolution-time/generateChart.ts \
--input "PR:pr-branch/apps/resolution-time/results-random.json" \
--input "main:target-branch/apps/resolution-time/results-random.json" \
--input "release:release-branch/apps/resolution-time/results-random.json" \
--title "Random Branching" \
--xAxisTitle "max depth" \
--yAxisTitle "time (ms)" \
--output chart-random.md

- name: Generate chart (linear)
run: |
node pr-branch/apps/resolution-time/generateChart.ts \
--input "PR:pr-branch/apps/resolution-time/results-linear-recursion.json" \
--input "main:target-branch/apps/resolution-time/results-linear-recursion.json" \
--input "release:release-branch/apps/resolution-time/results-linear-recursion.json" \
--title "Linear Recursion" \
--xAxisTitle "max depth" \
--yAxisTitle "time (ms)" \
--output chart-linear.md

- name: Generate chart (max depth)
run: |
node pr-branch/apps/resolution-time/generateChart.ts \
--input "PR:pr-branch/apps/resolution-time/results-max-depth.json" \
--input "main:target-branch/apps/resolution-time/results-max-depth.json" \
--input "release:release-branch/apps/resolution-time/results-max-depth.json" \
--title "Full Tree" \
--xAxisTitle "max depth" \
--yAxisTitle "time (ms)" \
--output chart-max-depth.md

- name: Prepare comment
run: |
{
echo "## Resolution Time Benchmark"
echo ""
cat chart-random.md
echo ""
cat chart-linear.md
echo ""
cat chart-max-depth.md
} > comparison.md

- name: Comment PR with results
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const comparison = fs.readFileSync('comparison.md', 'utf8');

const botCommentIdentifier = '<!-- resolution-time-bot-comment -->';

async function findBotComment(issueNumber) {
if (!issueNumber) return null;
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
});
return comments.data.find((comment) =>
comment.body.includes(botCommentIdentifier)
);
}

async function createOrUpdateComment(issueNumber) {
if (!issueNumber) {
console.log('No issue number provided. Cannot post or update comment.');
return;
}

const existingComment = await findBotComment(issueNumber);
if (existingComment) {
await github.rest.issues.updateComment({
...context.repo,
comment_id: existingComment.id,
body: botCommentIdentifier + '\n' + comparison,
});
} else {
await github.rest.issues.createComment({
...context.repo,
issue_number: issueNumber,
body: botCommentIdentifier + '\n' + comparison,
});
}
}

const issueNumber = context.issue.number;
if (!issueNumber) {
console.log('No issue number found in context. Skipping comment.');
} else {
await createOrUpdateComment(issueNumber);
}
await core.summary
.addRaw(comparison)
.write();
4 changes: 2 additions & 2 deletions apps/resolution-time/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ const myLeafFn = tgpu.comptime(() => {

### 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.
Use `tgpu.unroll` over `std.range(n)` 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))) {
for (const _i of tgpu.unroll(std.range(n))) {
instructions[choice()]()();
}
popDepth(); // REQUIRED — always call at the end, after the unroll
Expand Down
38 changes: 12 additions & 26 deletions apps/resolution-time/procedural.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface ProcGenConfig {
}

// default config
const SAMPLES = 10;
const SAMPLES = 50;
const config: ProcGenConfig & { samples: number } = {
mainBranching: 2,
branching: 2,
Expand Down Expand Up @@ -45,10 +45,6 @@ const state = tgpu.lazy(() => ({
const instructions: TgpuComptime<() => () => void>[] = [];
const LEAF_COUNT = 4;

// TODO: replace it with number, when unroll supports that
const getArrayForUnroll = tgpu.comptime((n: number) => Array.from({ length: n }));
let branchingUnrollArray = getArrayForUnroll(config.branching);

const choice = tgpu.comptime((): number => {
if (state.$.stackDepth == config.maxDepth - 1 || rand() > config.recurseProb) {
state.$.stackDepth++;
Expand Down Expand Up @@ -156,7 +152,7 @@ const waveFn = tgpu.comptime(() => {
v = d.vec2f(std.sin(v.x * Math.PI), std.cos(v.y * Math.PI));
const _energy = std.dot(v, v);

for (const _i of tgpu.unroll(branchingUnrollArray)) {
for (const _i of tgpu.unroll(std.range(config.branching))) {
// @ts-expect-error trust me
instructions[choice()]()();
}
Expand All @@ -176,7 +172,7 @@ const accFn = tgpu.comptime(() => {
let acc = d.vec2f();
acc = d.vec2f(acc.x + offset.x * scale, acc.y + offset.y * scale);

for (const _i of tgpu.unroll(branchingUnrollArray)) {
for (const _i of tgpu.unroll(std.range(config.branching))) {
// @ts-expect-error trust me
instructions[choice()]()();
}
Expand All @@ -198,7 +194,7 @@ const rotateFn = tgpu.comptime(() => {
const s = std.sin(angle);
v = d.vec2f(v.x * c - v.y * s, v.x * s + v.y * c);

for (const _i of tgpu.unroll(branchingUnrollArray)) {
for (const _i of tgpu.unroll(std.range(config.branching))) {
// @ts-expect-error trust me
instructions[choice()]()();
}
Expand All @@ -220,7 +216,7 @@ const spiralFn = tgpu.comptime(() => {
const pos = d.vec2f(center.x + radius * std.cos(angle), center.y + radius * std.sin(angle));
const _dist = std.length(pos);

for (const _i of tgpu.unroll(branchingUnrollArray)) {
for (const _i of tgpu.unroll(std.range(config.branching))) {
// @ts-expect-error trust me
instructions[choice()]()();
}
Expand All @@ -236,7 +232,7 @@ instructions.push(baseFn, blendFn, thresholdFn, filterFn, waveFn, accFn, rotateF
const main = () => {
'use gpu';

for (const _i of tgpu.unroll(getArrayForUnroll(config.mainBranching))) {
for (const _i of tgpu.unroll(std.range(config.mainBranching))) {
// @ts-expect-error trust me
instructions[choice()]()();
}
Expand All @@ -259,10 +255,15 @@ const outDir = resolve(import.meta.dirname ?? '.', '.');

function runBenchmark(input: ProcGenConfig, output: BenchmarkResult[]) {
Object.assign(config, { samples: input.samples ?? SAMPLES }, input);
branchingUnrollArray = getArrayForUnroll(config.branching);

// warmup
for (let i = 0; i < config.samples; i++) {
rand = splitmix32(config.seed);
benchmarkResolve();
}

for (let i = 0; i < config.samples; i++) {
rand = splitmix32((config.seed << i) | (config.seed >> (32 - i)));
const result = benchmarkResolve();
output.push(result);
console.log(
Expand All @@ -273,21 +274,6 @@ function runBenchmark(input: ProcGenConfig, output: BenchmarkResult[]) {
}
}

function warmupJIT() {
runBenchmark(
{
mainBranching: 1,
branching: 1,
maxDepth: 1,
recurseProb: 0,
seed: 0.1882 * 2 ** 32,
},
[],
);
}

warmupJIT();

const results: BenchmarkResult[] = [];
const DEPTHS = Array.from({ length: 8 }, (_, i) => i + 1);

Expand Down
Loading