Task 2 of 5
Run the last task sixteen times over, once per bin, and you have the whole
histogram. output: [16] launches sixteen threads; thread x owns bin
x, scans the entire array, and counts the codes that belong to it. Nobody writes
into anybody else's cell, so there is nothing left to race over. The scatter became a
gather — the same move Thinking in Parallel makes, wearing its most useful
disguise.
Say the price out loud, because it is real: every one of the 16 threads reads all 4,096 codes, so this histogram costs n × bins reads where the CPU's cost n. You bought correctness with redundant work. On a GPU that is very often the right trade — the redundant reads run in parallel and hit cache, while the serialization an atomic costs does not parallelize at all — but it stops being the right trade as the bin count grows, and task 4 fixes the other end of it.
One check catches almost every histogram bug ever written, so build the habit now: the counts must sum to the number of inputs. Every input belongs to exactly one bin, so 4,096 codes must produce counts totalling 4,096. Anything else means values are being dropped or double-counted, and the size of the gap usually tells you which.
output: [16] — one thread per bin, no loop over the binsthis.constants.n codes and counts only the ones equal to this.thread.xconsole.log the total (it should come to 4096)this.thread.x is both this thread's output cell and the
code it is counting. That coincidence is the entire kernel: thread 5 counts the 5s.
if (codes[i] === this.thread.x) count++;A plain loop after the kernel call:
let total = 0;
for (let b = 0; b < counts.length; b++) {
total += counts[b];
}
If that is not 4096, stop and find out why before you trust a single bar.
This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.