4 ms·
Further optimization potential: the four lines sum = Avx2.Add(block0, sum); sum = Avx2.Add(block1, sum); sum = Avx2.Add(block2, sum); sum = Avx
by zvrba 8y ago
Further optimization potential: the four lines
sum = Avx2.Add(block0, sum);
sum = Avx2.Add(block1, sum);
sum = Avx2.Add(block2, sum);
sum = Avx2.Add(block3, sum);
have all a serializing dependency on sum variable. But (integer) addition is associative and commutative, so you could sum it in a tree-like manner, ending up only with a a single serializing dependency:
sum01 = Avx.Add(block0, block1);
sum23 = Avx.Add(block2, block3); // These two run in parallel
sum = Avx.Add(sum, sum01); // sum01 hopefully ready; parallel with sum23
sum = Avx.Add(sum, sum23); // sum23 hopefully ready
Where only the last line serializes with the previous one. Maybe the HW is smart enough to rename the registers and do the same thing internally, but it'd be interesting to benchmark it.
- Metalnem 8y agoI already tried that, but was disappointed that the performance gain was only 1%, which is why I didn't include the optimization in the post.
- physguy1123 8y agoYou should try maintaining 4 independent sum variables and summing after the loop so there's no serializing dependency at all. Such a transformation in microbenchmarks is a fun trick to show the power of a proper OOO engine with pipelined instruction units. Assuming no memory problems, one should be able use issue-width*instruction latency independent sum streams without spending more time in the hot loop. For what it's worth, the vmovdqa only has a 4-wide issue width if it is moving between registers, the memory load has a 2-wide issue width. Floating point adders themselves only have a 1-2 wide issue widths depending on your hardware so it doesn't really matter.