
only count up to one second is not exactly a useful stopwatch. For convenience,
these fragments will be called ”counting fragments.”
The sole fragment that does not increment the counter instead reads from the
counter, performs a task, then reads it again. Finally, if the working fragment
takes the di↵erence between the two counter values it read and outputs the
result to a bu↵er, the resulting value serves as benchmark of the time the task
took compared to how quickly the counter was incremented. The result does
not directly equate a wall time, but it serves as an implicit time taken by the
task. This fragment will be called the ”working fragment.”
To choose which fragment to designate as the working fragment, observe from
the first set of experiments that the bottom-left pixels are generally rendered
first. Since the working fragment should begin is task as the shared counter
starts incrementing, and finish the task before all the counting fragments stop
counting, we chose the working fragment to be in the bottom-left corner. The
task selected for the working fragment should be a calculation with a difficult-
to-predict result, to avoid any compiler optimizations of the shader code that
may interfere with the calculation speed.
If the counter is incremented at a consistent pace, we can expect the di↵er-
ence between the two observed counts to increase linearly with the task size -
that is, if some task is done twice, the counter di↵erence should increase twofold.
If the task is done ten times, the counter di↵erence should increase tenfold.
3.4 Test Set 2: Timing Tasks with Atomic Counters
The second set of tests used the technique from the previous subsection to time
an arbitrary task - in this case, calculating sine values.
The tests were all conducted on images of 300 by 300 pixels in size for a total
of 89,999 counting pixels. In practice, this was roughly the smallest size image
before results began deviating downwards from the consistent results from larger
sizes, perhaps indicating that there was not enough contention for the counter,
or that the counting fragments finished counting too quickly.
Each counting pixel was asked to increment the shared atomic counter 3,000
times, for a total count of 269,997,000. Notably, some cards were able to handle
upwards of 100,000 increments per fragment, while others, once tasked with
incrementing more than 5,000 times each, would cause WebGL to throw errors,
stop rendering, freeze the screen, or flash the entire screen black. This is in
and of itself a fingerprint - how high each fragment in a card can increment
9