False Sharing

Two threads incrementing two different variables should not interfere with each other. If the two variables happen to land in the same cache line, they do: every write by one core invalidates the other core's copy of the line, and the line shuttles between them. Nothing is shared in the program. Everything is shared in the hardware. Every other page about this shows you a number from somebody else's laptop. This one measures yours.

Nothing is shared, and they fight anyway

1 What this browser will allow, including how fine a clock it is willing to show

What this browser will let the measurement use. A page that cannot measure has to say so rather than print a number nobody took.

WhatHere

Shared memory and a fine clock were both taken away after Spectre, and both come back only for a page that is cross-origin isolated. This one asks for that isolation on its own path rather than imposing it on the rest of the studio.

2 Two threads incrementing their own counters, trials alternating between the two layouts

Two threads, each incrementing its own counter four hundred thousand times. Twenty-four trials, alternating between counters that share a cache line and counters that do not, so that anything drifting on this machine lands on both.

3 What the trials took, as a spread rather than a number, and whether the difference is real here

What the trials took

LayoutMedianMiddle halfTrials

The middle half is shown because a single number is not a measurement. The rule is deliberately blunt: if the middle halves overlap, or the gap is smaller than a few ticks of this clock, the page reports no measurable difference. That is a statement about these trials on this machine, not a confidence interval, and it is more useful than a ratio that flatters the page.

4 How far apart the counters have to be before the cost stops, which is a measurement of this machine and not quite the cache line size

How far apart they have to be

Counters apart byTime

These ran in this browser when the page loaded. Each claim, whether it held, and the number behind it.

Each claim, whether it held, and the values behind it
claimheldmeasured
one cache line is 64 bytes, so the padded layout skips 16 slotsyes16 slots of 4 bytes
the clock's granularity is measured rather than assumedyessmallest observable step 0.1000 ms
the summary reports the median, not the meanyesmedian 6 of 2,4,6,8,10; mean would be 6 too, so also: median 1 of 1,1,1,97
overlapping timings are reported as no measurable differenceyesmedians 30 and 32, ranges overlap
and separated timings are reported as a real oneyesmedians 102 and 12, ranges apart
a gap smaller than the clock can resolve is credited to the clockyesthe same two timings, with a clock whose tick is 40 ms
a trial where the two threads ran together for most of the time countsyesone ran 0 to 10, the other 1 to 11, sharing 90% of the shorter run
one where they barely overlapped is discarded, not averaged inyesshared only 10%, below the 80% floor
and one where they did not overlap at all is discardedyesone finished at 10, the other started at 20: nothing was contended
a trial lasts from the first thread starting to the last one stoppingyes0 to 30 and 0 to 11, not either thread's own stretch
this browser cannot run the measurement, and the page says so instead of inventing oneyesSharedArrayBuffer is not available. It was disabled in every browser after Spectre and comes back only for pages that are cross-origin isolated.

What is real here, and what is not

This is a measurement, and it can fail

Three ways, all of which the page says out loud. The browser may withhold shared memory, in which case nothing is measured. The two threads may not overlap, in which case that trial contended for nothing and is discarded and counted. And the difference may be smaller than this machine's noise, in which case the answer is that it was not measurable here. Only the third is a result; the other two are the page refusing to invent one.

The clock is deliberately coarse and its granularity is measured, not assumed

performance.now() was blunted in every browser after Spectre, and cross-origin isolation is what sharpens it again. The page measures the smallest step the clock will show and reports it, because a measurement whose noise floor is unstated is not a measurement. A difference smaller than a few of those steps is credited to the clock rather than to the cache.

Atomics, not a plain increment

A plain increment in a loop can be optimised out of the loop entirely, and then the page measures nothing while printing a number. An atomic read-modify-write cannot be elided, and it is also the operation that genuinely contends for the line.

The trials alternate rather than running in batches

All the shared trials followed by all the padded ones would let processor frequency scaling, or whatever else this machine is doing, land entirely on one group and read as a cache effect. Alternating spreads it across both.

What the sweep settles, and what it does not

Sixty-four bytes is the usual answer on x86-64 and Apple's cores use a hundred and twenty-eight in places. Rather than assert either, the page sweeps the distance between the two counters and reports where the cost falls away. That distance is a real measurement of this machine and it is NOT the cache line size. The counters sit at a fixed offset into a buffer whose physical alignment JavaScript will not reveal, so a fall at thirty-two bytes can mean a sixty-four byte line whose boundary happens to lie between them. An earlier version of this page printed that number and called it the cache line, which was an artifact of where the counters were put. What the sweep does settle is the practical question: how far apart two variables have to be before they stop costing each other anything, on this processor, today. If nothing falls by more than the clock can resolve, the page says that instead.

Sound: no

The measurement is a ratio of durations in microseconds, which cannot be played at true scale. That is the same reason CPU Cache did not earn sound, and the studio's rule is that a sound carries the measurement or is not built.

The checks below test the reporting, not your processor

Every machine here ends with a table of claims checked when the page loaded. On this page those checks feed the summary and verdict functions SYNTHETIC numbers with known answers, because that is the only way to test a reporter: it confirms that overlapping spreads are called unmeasurable, that a gap smaller than the clock is credited to the clock, and that a trial whose threads did not overlap is discarded. None of it is a measurement of your machine. The measurement is the part you press a button for.

What is still not controlled

Which cores the browser places the two workers on, whether it places them on the same physical core, what else the machine is doing, and how the processor is scaling its frequency. None of that is reachable from JavaScript. The trials warm up first, alternate between layouts and discard any pair that did not overlap, which handles what can be handled and leaves the rest as a reason the answer here may differ from the answer ten minutes from now.

Sources