Parallelism, memory accesses and vectorization
Test configuration used for benchmarking in this talk
| quantity | value |
|---|---|
| stations | 68 |
| baselines | 2 278 |
| channels | 416 |
| sky components | 25 085 |
| polarization | unpolarized (Stokes I only) |
| patches | 14 365 |
| timesteps | 100 |
| machine | 72 cores (2x Intel Xeon Platinum 8360Y) |
Structure:
Potential issues:
| metric | value |
|---|---|
| predict | 1 161 s |
| wall | 1 612 s |
| user CPU | 66 623 s |
| mean parallelism | 41.3× of 72 (57%) |
simulate() itself only spreads 1.7x — 93% of components are Gaussian, so an equal-count split gives every thread much the same mixtbb::parallel_for instead of a fixed slice per thread6–24% faster than the static baseline at every measured thread count: 24% at 24 threads, but 7% at 72. Still far short of what the profiling budget suggested was available.
| metric | static split | TBB stealing |
|---|---|---|
| thread utilization | 77.4% | 99.6% |
| time spread | 64.2% | 0.6% |
| idle, per timestep | 1.88s | 0.03s |
Over a thread’s whole work, per timestep, averaged over 28 timesteps.
Structural redundancy: a patch split across a range boundary is applied once in each range — applies ≈ patches + ranges, so the finer the stealing, the more applies
Four variants tried, verdicts under each panel
==> General conclusion: tradeoff between parallelism efficiency and memory overhead
| metric | static | TBB |
|---|---|---|
| Memory Bound | 77% | 76% |
| L3 Bound | 1.4% | 1.3% |
| DRAM Bound | 16.7% | 19.3% |
| Load ops | 1.9T | 2.1T |
| LLC misses | 4.8B | 5.1B |
Work stealing moves +6% LLC misses in our test case.
| original | tiled | ||
|---|---|---|---|
| memory bound | 77% | 13% | 6.0× |
| DRAM bound | 17% | 3% | 5.8× |
| store bound | 10.6% | 0.9% | 11.8× |
| LLC misses | 4.8 B | 0.33 B | 14.6× |
Memory traffic analysis of the simulation phase of the prediction step.
xsimd::sincos in phases() and xsimd::exp in the Gaussian envelopesimulate()