This is just an idea worth investigating.
On GPU, we are currently launching the calculate_jamps kernel with the following configuration:
- threads:
(256, 1)
- blocks:
(nevents // 256, nhelicities)
This means that each block gets a single value of helicity, so each event in a single block manages 1 helicity.
What if we invert this parallelism, and we make each block manage 1 event and all the helicities?
This might be useful, likely, only for those processes with many helicity combinations (read: high multiplicity), so we might need to have a (compile-time) switch (as soon as we understand what is the sweet spot) that selects if keeping launch parameters as they are, or inverting this parallelism.
This idea is justified by:
- the GPU kernel being branchless on the point of view of the helicity value, so it can run in perfect lockstep even if threads are managing different helicity values;
- the fact that we would have one event per block, that we can store in shared memory: each thread in a block will then just need to load the same
nparticles * np4 * 2 double numbers from shared memory and 1 integer (the helicity value) from constant memory, instead of having different nparticles * np4 * 2 and anyway 1 integer per thread; at the block level this reduces the memory required to load all the momenta in a block (and consequently in a warp), possibly reducing the registers pressure.
Also intermediate scenarios could be studied, for example:
- threads:
(256 // n, n)
- blocks:
(nevents // (256 // n), nhelicities // n)
in which we would make use of a 2-dimensional grid approach, which might or not be more efficient in some scenarios.
I'd propose to study that on higher multiplicity processes such as g g > t t~ g g, and g g > t t~ g g g, by comparing the difference in throughput using the standalone exporter.
NSight Compute can then be used to study the warp scheduling and occupancy.
This is just an idea worth investigating.
On GPU, we are currently launching the
calculate_jampskernel with the following configuration:(256, 1)(nevents // 256, nhelicities)This means that each block gets a single value of helicity, so each event in a single block manages 1 helicity.
What if we invert this parallelism, and we make each block manage 1 event and all the helicities?
This might be useful, likely, only for those processes with many helicity combinations (read: high multiplicity), so we might need to have a (compile-time) switch (as soon as we understand what is the sweet spot) that selects if keeping launch parameters as they are, or inverting this parallelism.
This idea is justified by:
nparticles * np4 * 2double numbers from shared memory and 1 integer (the helicity value) from constant memory, instead of having differentnparticles * np4 * 2and anyway 1 integer per thread; at the block level this reduces the memory required to load all the momenta in a block (and consequently in a warp), possibly reducing the registers pressure.Also intermediate scenarios could be studied, for example:
(256 // n, n)(nevents // (256 // n), nhelicities // n)in which we would make use of a 2-dimensional grid approach, which might or not be more efficient in some scenarios.
I'd propose to study that on higher multiplicity processes such as
g g > t t~ g g, andg g > t t~ g g g, by comparing the difference in throughput using thestandaloneexporter.NSight Compute can then be used to study the warp scheduling and occupancy.