Skip to content

Commit d4f96b8

Browse files
committed
gpu puzzles web app cleanup pass (todo: fix async bug for non-debug builds)
1 parent 714be01 commit d4f96b8

4 files changed

Lines changed: 119 additions & 318 deletions

File tree

Lines changed: 108 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,85 @@
11
const State = {
2-
preamble_template : "",
3-
preamble : "",
4-
wgSize : [1, 1, 1],
5-
gridSize : [1, 1, 1],
6-
editor : null,
7-
terminal : null,
8-
module : null,
9-
isModuleReady : false,
10-
checkAnswer : false,
11-
isDispatchReady : true, // don't allow multiple overlapping dispatches
12-
puzzleIndex : 0,
2+
preamble_template: "",
3+
preamble: "",
4+
editor: null,
5+
terminal: null,
6+
module: null,
7+
isModuleReady: false,
8+
checkAnswer: false,
9+
isDispatchReady: true, // don't allow multiple overlapping dispatches
10+
puzzleIndex: 0,
1311
};
1412

1513
const PuzzleSpec = [
1614
{
17-
name : "Map",
18-
description :
19-
'Implement a "kernel" (GPU function) that adds 10 to each position of vector `a` and stores it in vector `out`. You have 1 thread per position.',
15+
name: "Map",
16+
description:
17+
'Implement a "kernel" (GPU function) that adds 10 to each position of vector `a` and stores it in vector `out`. You have 1 thread per position.',
2018
},
2119
{
22-
name : "Zip",
23-
description :
24-
"Implement a kernel that adds together each position of `a` and `b` and stores it in `out`. You have 1 thread per position.",
20+
name: "Zip",
21+
description:
22+
"Implement a kernel that adds together each position of `a` and `b` and stores it in `out`. You have 1 thread per position.",
2523
},
2624
{
27-
name : "Guards",
28-
description :
29-
"Implement a kernel that adds 10 to each position of `a` and stores it in `out`. You have more threads than positions.",
25+
name: "Guards",
26+
description:
27+
"Implement a kernel that adds 10 to each position of `a` and stores it in `out`. You have more threads than positions.",
3028
},
3129
{
32-
name : "Map 2D",
33-
description :
34-
"Implement a kernel that adds 10 to each position of `a` and stores it in `out`. Input `a` is 2D and square. You have more threads than positions.",
30+
name: "Map 2D",
31+
description:
32+
"Implement a kernel that adds 10 to each position of `a` and stores it in `out`. Input `a` is 2D and square. You have more threads than positions.",
3533
},
3634
{
37-
name : "Broadcast",
38-
description :
39-
"Implement a kernel that adds `a` and `b` and stores it in `out`. Inputs `a` and `b` are vectors. You have more threads than positions.",
35+
name: "Broadcast",
36+
description:
37+
"Implement a kernel that adds `a` and `b` and stores it in `out`. Inputs `a` and `b` are vectors. You have more threads than positions.",
4038
},
4139
{
42-
name : "Blocks",
43-
description :
44-
"Implement a kernel that adds 10 to each position of `a` and stores it in `out`. You have fewer threads per block than the size of `a`.",
40+
name: "Blocks",
41+
description:
42+
"Implement a kernel that adds 10 to each position of `a` and stores it in `out`. You have fewer threads per block than the size of `a`.",
4543
},
4644
{
47-
name : "Blocks 2D",
48-
description :
49-
"Implement the same kernel in 2D. You have fewer threads per block than the size of `a` in both directions.",
45+
name: "Blocks 2D",
46+
description:
47+
"Implement the same kernel in 2D. You have fewer threads per block than the size of `a` in both directions.",
5048
},
5149
{
52-
name : "Shared",
53-
description :
54-
"Implement a kernel that adds 10 to each position of `a` and stores it in `out`. You have fewer threads per block than the size of `a`. Use shared memory and `cuda.syncthreads` to ensure threads do not cross.",
50+
name: "Shared",
51+
description:
52+
"Implement a kernel that adds 10 to each position of `a` and stores it in `out`. You have fewer threads per block than the size of `a`. Use shared memory and `cuda.syncthreads` to ensure threads do not cross.",
5553
},
5654
{
57-
name : "Pooling",
58-
description :
59-
"Implement a kernel that sums together the last 3 positions of `a` and stores it in `out`. You have 1 thread per position.",
55+
name: "Pooling",
56+
description:
57+
"Implement a kernel that sums together the last 3 positions of `a` and stores it in `out`. You have 1 thread per position.",
6058
},
6159
{
62-
name : "Dot Product",
63-
description :
64-
"Implement a kernel that computes the dot-product of `a` and `b` and stores it in `out`. You have 1 thread per position.",
60+
name: "Dot Product",
61+
description:
62+
"Implement a kernel that computes the dot-product of `a` and `b` and stores it in `out`. You have 1 thread per position.",
6563
},
6664
{
67-
name : "1D Convolution",
68-
description :
69-
"Implement a kernel that computes a 1D convolution between `a` and `b` and stores it in `out`. Handle the general case.",
65+
name: "1D Convolution",
66+
description:
67+
"Implement a kernel that computes a 1D convolution between `a` and `b` and stores it in `out`. Handle the general case.",
7068
},
7169
{
72-
name : "Prefix Sum",
73-
description :
74-
"Implement a kernel that computes a sum over `a` and stores it in `out`. If the size of `a` is greater than the block size, only store the sum of each block using parallel prefix sum.",
70+
name: "Prefix Sum",
71+
description:
72+
"Implement a kernel that computes a sum over `a` and stores it in `out`. If the size of `a` is greater than the block size, only store the sum of each block using parallel prefix sum.",
7573
},
7674
{
77-
name : "Axis Sum",
78-
description :
79-
"Implement a kernel that computes a sum over each column of `a` and stores it in `out`.",
75+
name: "Axis Sum",
76+
description:
77+
"Implement a kernel that computes a sum over each column of `a` and stores it in `out`.",
8078
},
8179
{
82-
name : "Matrix Multiply",
83-
description :
84-
"Implement a kernel that multiplies square matrices `a` and `b` and stores the result in `out`. Optimize by using shared memory for partial dot-products.",
80+
name: "Matrix Multiply",
81+
description:
82+
"Implement a kernel that multiplies square matrices `a` and `b` and stores the result in `out`. Optimize by using shared memory for partial dot-products.",
8583
},
8684
];
8785

@@ -113,11 +111,11 @@ function initializeEditor(initialContent) {
113111
AppState.editor.setTheme("ace/theme/dracula");
114112
AppState.editor.session.setMode("ace/mode/javascript");
115113
AppState.editor.setOptions({
116-
fontSize : "16px",
117-
showPrintMargin : false,
118-
showGutter : false,
119-
highlightActiveLine : true,
120-
wrap : true,
114+
fontSize: "16px",
115+
showPrintMargin: false,
116+
showGutter: false,
117+
highlightActiveLine: true,
118+
wrap: true,
121119
});
122120
AppState.editor.setKeyboardHandler("ace/keyboard/vim");
123121
AppState.editor.setValue(initialContent || "");
@@ -127,39 +125,29 @@ function initializeEditor(initialContent) {
127125

128126
function initializeModule() {
129127
createModule()
130-
.then((Module) => {
131-
AppState.module = Module;
132-
AppState.module.print = customPrint;
133-
AppState.module.printErr = customPrint;
134-
AppState.isModuleReady = true;
135-
console.log("Module initialized");
136-
update({type : "init"});
137-
})
138-
.catch(
139-
(error) => { console.error("Failed to initialize module:", error); });
128+
.then((Module) => {
129+
AppState.module = Module;
130+
AppState.module.print = customPrint;
131+
AppState.module.printErr = customPrint;
132+
AppState.isModuleReady = true;
133+
console.log("Module initialized");
134+
update({ type: "init" });
135+
})
136+
.catch((error) => {
137+
console.error("Failed to initialize module:", error);
138+
});
140139
}
141140

142141
function setupEventListeners() {
143-
AppState.editor.session.on("change", () => update({type : "edit"}));
142+
AppState.editor.session.on("change", () => update({ type: "edit" }));
144143
window.addEventListener("resize", () => AppState.editor.resize());
145-
/*
146-
document.getElementById("workgroup_x")
147-
.addEventListener("change", () => update({type : "wgUpdate"}));
148-
document.getElementById("workgroup_y")
149-
.addEventListener("change", () => update({type : "wgUpdate"}));
150-
document.getElementById("workgroup_z")
151-
.addEventListener("change", () => update({type : "wgUpdate"}));
152-
document.getElementById("grid_x").addEventListener(
153-
"change", () => update({type : "gridUpdate"}));
154-
document.getElementById("grid_y").addEventListener(
155-
"change", () => update({type : "gridUpdate"}));
156-
document.getElementById("grid_z").addEventListener(
157-
"change", () => update({type : "gridUpdate"}));
158-
*/
159-
document.getElementById("prev").addEventListener(
160-
"click", () => { update({type : "selectPuzzle", value : "prev"}); });
161-
document.getElementById("next").addEventListener(
162-
"click", () => { update({type : "selectPuzzle", value : "next"}); });
144+
145+
document.getElementById("prev").addEventListener("click", () => {
146+
update({ type: "selectPuzzle", value: "prev" });
147+
});
148+
document.getElementById("next").addEventListener("click", () => {
149+
update({ type: "selectPuzzle", value: "next" });
150+
});
163151
}
164152

165153
////////////////////////////////////////
@@ -179,30 +167,6 @@ function customPrint(text) {
179167
// Update
180168
////////////////////////////////////////
181169

182-
/*
183-
function updateDispatchParams() {
184-
wgSize = [
185-
document.getElementById("workgroup_x").value,
186-
document.getElementById("workgroup_y").value,
187-
document.getElementById("workgroup_z").value,
188-
];
189-
gridSize = [
190-
document.getElementById("grid_x").value,
191-
document.getElementById("grid_y").value,
192-
document.getElementById("grid_z").value,
193-
];
194-
wgSize = wgSize.map((x) => parseInt(x));
195-
gridSize = gridSize.map((x) => parseInt(x));
196-
AppState.wgSize = wgSize;
197-
AppState.gridSize = gridSize;
198-
AppState.preamble = AppState.preamble_template.replace(
199-
/{{workgroupSize}}/g,
200-
wgSize.join(", "),
201-
);
202-
console.log("New preamble:\n", AppState.preamble);
203-
}
204-
*/
205-
206170
function waitForDispatchReady() {
207171
return new Promise((resolve) => {
208172
function checkReady() {
@@ -218,45 +182,39 @@ function waitForDispatchReady() {
218182
}
219183

220184
async function updateEditor() {
221-
222185
// Recover from errors TODO(avh): only do this if there's an error
223-
createModule().then(
224-
(Module) => { console.log("updateEditor() - Module ready"); });
186+
createModule().then((Module) => {
187+
console.log("updateEditor() - Module ready");
188+
});
225189
if (AppState.module) {
226190
if (!AppState.isDispatchReady) {
227191
await waitForDispatchReady();
228192
}
229193
console.log("Executing kernel");
230194
AppState.terminal.clear();
231-
// console.log("Code:\n", AppState.preamble + AppState.editor.getValue());
232195
code = AppState.editor.getValue();
233196
AppState.isDispatchReady = false;
234197
try {
235198
promise = AppState.module
236-
.evaluate(
237-
code,
238-
AppState.wgSize,
239-
AppState.gridSize,
240-
AppState.puzzleIndex,
241-
)
242-
.catch((error) => {
243-
console.error("execution failed", error);
244-
AppState.isDispatchReady = true;
245-
console.log("dispatch ready");
246-
render();
247-
})
248-
.then((result) => {
249-
console.log("check:", result);
250-
AppState.checkAnswer = result;
251-
AppState.isDispatchReady = true;
252-
console.log("dispatch ready");
253-
render();
254-
})
255-
.finally(() => {
256-
console.log("finally");
257-
AppState.isDispatchReady = true;
258-
console.log("dispatch ready");
259-
});
199+
.evaluate(code, AppState.puzzleIndex)
200+
.catch((error) => {
201+
console.error("execution failed", error);
202+
AppState.isDispatchReady = true;
203+
console.log("dispatch ready");
204+
render();
205+
})
206+
.then((result) => {
207+
console.log("check:", result);
208+
AppState.checkAnswer = result;
209+
AppState.isDispatchReady = true;
210+
console.log("dispatch ready");
211+
render();
212+
})
213+
.finally(() => {
214+
console.log("finally");
215+
AppState.isDispatchReady = true;
216+
console.log("dispatch ready");
217+
});
260218
} catch (error) {
261219
console.error("execution failed 2", error);
262220
AppState.isDispatchReady = true;
@@ -294,14 +252,12 @@ function update(event) {
294252
AppState.puzzleIndex = 0;
295253
}
296254
}
297-
if ((event.type === "init") || (event.type === "selectPuzzle")) {
255+
if (event.type === "init" || event.type === "selectPuzzle") {
256+
// Reset editor template code if we are either starting the app for the
257+
// first time or picking a new puzzle
298258
AppState.editor.setValue(AppState.module.getTemplate(AppState.puzzleIndex));
299259
}
300-
// AppState.wgSize = AppState.module.getWorkgroupSize(AppState.puzzleIndex);
301-
// AppState.gridSize = AppState.module.getNumWorkgroups(AppState.puzzleIndex);
302260

303-
// updateDispatchParams(); // comment this out since it should be set by the
304-
// wasm getters
305261
updateEditor();
306262
render();
307263
}
@@ -311,34 +267,22 @@ function update(event) {
311267
////////////////////////////////////////
312268

313269
function render() {
314-
console.log("AppState.wgSize: ", AppState.wgSize);
315-
/*
316-
document.getElementById("workgroup_x").value = AppState.wgSize[0];
317-
document.getElementById("workgroup_y").value = AppState.wgSize[1];
318-
document.getElementById("workgroup_z").value = AppState.wgSize[2];
319-
document.getElementById("grid_x").value = AppState.gridSize[0];
320-
document.getElementById("grid_y").value = AppState.gridSize[1];
321-
document.getElementById("grid_z").value = AppState.gridSize[2];
322-
console.log("AppState.wgSize: ", AppState.wgSize);
323-
console.log("AppState.gridSize: ", AppState.gridSize);
324-
*/
325-
326270
console.log("AppState.checkAnswer: ", AppState.checkAnswer);
327-
document.getElementById("correct").textContent =
328-
AppState.checkAnswer ? "Tests passed!"
329-
: "Some tests failed.";
330-
// make colors red if incorrect
271+
document.getElementById("correct").textContent = AppState.checkAnswer
272+
? "Tests passed!"
273+
: "Some tests failed.";
331274
if (AppState.checkAnswer) {
332275
document.getElementById("correct").style.color = "LimeGreen";
333276
document.getElementById("correct").style.fontWeight = "bold";
334-
335277
} else {
336278
document.getElementById("correct").style.color = "red";
337279
document.getElementById("correct").style.fontWeight = "bold";
338280
}
339281
document.getElementById("puzzle_name").textContent =
340-
"Puzzle " + (AppState.puzzleIndex + 1) + ": " +
341-
PuzzleSpec[AppState.puzzleIndex].name;
282+
"Puzzle " +
283+
(AppState.puzzleIndex + 1) +
284+
": " +
285+
PuzzleSpec[AppState.puzzleIndex].name;
342286
document.getElementById("puzzle_description").textContent =
343-
PuzzleSpec[AppState.puzzleIndex].description;
287+
PuzzleSpec[AppState.puzzleIndex].description;
344288
}

0 commit comments

Comments
 (0)