Re: Unexplained variance in run-time of simple program (part 2)

Marc Gonzalez <[email protected]> Fri, 10 Apr 2026 19:16:25 +0200
Newsgroups org.kernel.vger.linux-rt-users
Message-ID <[email protected]>
On 08/04/2026 11:29, John D. McCalpin wrote:

> I am still not clear on exactly what you mean by “executing the benchmark 2^16 times”.
> Are you forking a new process 2^16 times, or is a single process executing a block of code 2^16 times.
> The difference is critical for determining where to look for sources of performance variability.
Based on your very insightful feedback (thanks again!) I currently use this wrapper:

#include <stdlib.h>
#include <stdio.h>

typedef unsigned int u32;

// Ignore id: process will be pinned to core 3
// Ignore hi: t1-t0 < 2^32 cycles (1.3 seconds)
static inline u32 rdtscp(void)
{
	u32 hi, lo, id;
	asm volatile("rdtscp" : "=d"(hi), "=a"(lo), "=c"(id));
	return lo;
}

extern void init(void);
extern void spin(void); // THE CODE UNDER BENCHMARK

static u32 v[4];

int main(int argc, char **argv)
{
	if (argc != 3) return 1;

	const u32 N = strtoul(argv[1], NULL, 10);
	const u32 S = strtoul(argv[2], NULL, 10);

	void *buf = aligned_alloc(4096, N * sizeof v);

	for (u32 n = 0; n < N; ++n)
	{
		for (u32 i = 0; i < 4; ++i)
		{
			u32 t0 = rdtscp();
			for (u32 s = 0; s < S; ++s) spin();
			u32 t1 = rdtscp();
			v[i] = t1 - t0;
		}
		__builtin_ia32_movntdq(buf + n * sizeof v, __builtin_ia32_movntdqa((void *)v));
	}

	for (int i = 0; i < N * 4; ++i) printf("%u\n", ((u32 *)buf)[i]);

	return 0;
}

N times: time the code 4 times, and transfer the 4 results to memory using a non-temporal store.
Once measuring is done, print all the results.
Would it be more efficient to write a full cache line with movntdq? (64 bytes vs 16 bytes)


The baseline code (to check for correct results) is:

spin:
	xor eax, eax
	mov ecx, 1000
loop:
	times 60 inc eax ; 120 bytes
	dec ecx
	jnz loop ; 8-bit offset
	ret


I ran the following experiments:
./run.sh ./baseline 200000 1
./run.sh ./baseline 200000 2
./run.sh ./baseline 200000 4


The results in histogram (in 50-cycle steps) :

histo.1
99000: 199999
99100: 1

histo.2
197950: 85290
198000: 114706
198050: 1
198200: 2
198250: 1

$ cat histo.4
395900: 110104
395950: 89891
396000: 1
396150: 2
396200: 1
396450: 1

1st col: cycles @ 3.3 GHz
2nd col: number of times result fell in that interval

I think this is looking pretty good!
I just need to test on my actual code that uses L1$.

Regards