add comment about cache analysis

This commit is contained in:
NunoSempere 2024-01-13 12:47:14 +01:00
parent 48f333adfe
commit 8e2f918dd3

View File

@ -51,7 +51,6 @@ void sampler_parallel(double (*sampler)(uint64_t* seed), double* results, int n_
// - xorshift can't start with 0 // - xorshift can't start with 0
// - the seeds should be reasonably separated and not correlated // - the seeds should be reasonably separated and not correlated
cache_box[i].seed = (uint64_t)rand() * (UINT64_MAX / RAND_MAX); cache_box[i].seed = (uint64_t)rand() * (UINT64_MAX / RAND_MAX);
// printf("#%ld: %lu\n",i, *seeds[i]);
// Other initializations tried: // Other initializations tried:
// *seeds[i] = 1 + i; // *seeds[i] = 1 + i;
@ -69,17 +68,40 @@ void sampler_parallel(double (*sampler)(uint64_t* seed), double* results, int n_
for (int j = lower_bound_inclusive; j < upper_bound_not_inclusive; j++) { for (int j = lower_bound_inclusive; j < upper_bound_not_inclusive; j++) {
results[j] = sampler(&(cache_box[i].seed)); results[j] = sampler(&(cache_box[i].seed));
// In principle, these results[j] could also result in two threads competing for the same cache line. /*
// In practice, though, t starts at 0 and ends at T
// a) this would happen infrequently at t=0,
// b) trying to unroll loops actually makes the code slower thread i accesses: results[i*quotient +0],
// c) 8 results[j] are 8 doubles, which fit a cache line. If n_samples/n_threads thread i+1 acccesses: results[(i+1)*quotient +0]
at t=T
thread i accesses: results[(i+1)*quotient -1]
thread i+1 acccesses: results[(i+2)*quotient -1]
The results[j] that are directly adjacent are
results[(i+1)*quotient -1] (accessed by thread i at time T)
results[(i+1)*quotient +0] (accessed by thread i+1 at time 0)
and these are themselves adjacent to
results[(i+1)*quotient -2] (accessed by thread i at time T-1)
results[(i+1)*quotient +1] (accessed by thread i+1 at time 2)
If T is large enough, which it is, two threads won't access the same
cache line at the same time.
Pictorially:
at t=0 ....i.........I.........
at t=T .............i.........I
and the two never overlap
Note that results[j] is a double, a double has 8 bytes (64 bits)
8 doubles fill a cache line of 64 bytes.
So we specifically won't get problems as long as n_samples/n_threads > 8
n_threads is normally 16, so n_samples > 128
Note also that this is only a problem in terms of speed, if n_samples<128
the results are still computed, it'll just be slower
*/
} }
} }
} }
for (int j = divisor_multiple; j < n_samples; j++) { for (int j = divisor_multiple; j < n_samples; j++) {
results[j] = sampler(&(cache_box[0].seed)); results[j] = sampler(&(cache_box[0].seed));
// we can just reuse a seed, this isn't problematic because we are not doing multithreading // we can just reuse a seed,
// this isn't problematic because we;ve now stopped doing multithreading
} }
free(cache_box); free(cache_box);