[libcxx-commits] [libcxx] [libc++][pstl] Add serial implementations of std::inclusive_scan and std::transform_inclusive_scan (PR #236219)
Michael G. Kazakov via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Oct 30 03:31:05 PDT 2026
mikekazakov wrote:
I've ran some benchmarks to compare 1-pass and 3-pass approaches on two parallel algorithms, the branch is available here: https://github.com/mikekazakov/llvm-project/tree/compare_gh195548.
```
std::transform_inclusive_scan (test/benchmarks/numeric/pstl.transform_inclusive_scan.bench.cpp):
1-pass
std::transform_inclusive_scan(std::execution::par, vector<double>) (minimal)/64/real_time 1188 ns 1187 ns 223868 1.17x
std::transform_inclusive_scan(std::execution::par, vector<double>) (minimal)/65536/real_time 58880 ns 58589 ns 4691 0.98x
std::transform_inclusive_scan(std::execution::par, vector<double>) (minimal)/1048576/real_time 436580 ns 369797 ns 1135 1.49x
std::transform_inclusive_scan(std::execution::par, vector<double>) (minimal)/67108864/real_time 6647804 ns 5368902 ns 41 1.28x
std::transform_inclusive_scan(std::execution::par, vector<double>) (cheap)/64/real_time 2193 ns 1313 ns 100000 0.64x
std::transform_inclusive_scan(std::execution::par, vector<double>) (cheap)/65536/real_time 211928 ns 208055 ns 1301 1.18x
std::transform_inclusive_scan(std::execution::par, vector<double>) (cheap)/1048576/real_time 543795 ns 513778 ns 519 1.47x
std::transform_inclusive_scan(std::execution::par, vector<double>) (cheap)/67108864/real_time 23206383 ns 21313727 ns 11 0.99x
std::transform_inclusive_scan(std::execution::par, vector<double>) (expensive)/64/real_time 1348 ns 1348 ns 192789 1.09x
std::transform_inclusive_scan(std::execution::par, vector<double>) (expensive)/65536/real_time 212062 ns 202377 ns 1183 2.45x
std::transform_inclusive_scan(std::execution::par, vector<double>) (expensive)/1048576/real_time 1193322 ns 1098987 ns 232 1.19x
std::transform_inclusive_scan(std::execution::par, vector<double>) (expensive)/67108864/real_time 68030750 ns 63773500 ns 4 1.05x
3-pass
std::transform_inclusive_scan(std::execution::par, vector<double>) (minimal)/64/real_time 1397 ns 1396 ns 195545
std::transform_inclusive_scan(std::execution::par, vector<double>) (minimal)/65536/real_time 57998 ns 57917 ns 4766
std::transform_inclusive_scan(std::execution::par, vector<double>) (minimal)/1048576/real_time 650867 ns 560978 ns 581
std::transform_inclusive_scan(std::execution::par, vector<double>) (minimal)/67108864/real_time 8512934 ns 6684756 ns 41
std::transform_inclusive_scan(std::execution::par, vector<double>) (cheap)/64/real_time 1408 ns 1407 ns 195973
std::transform_inclusive_scan(std::execution::par, vector<double>) (cheap)/65536/real_time 251667 ns 248808 ns 1108
std::transform_inclusive_scan(std::execution::par, vector<double>) (cheap)/1048576/real_time 801594 ns 776860 ns 342
std::transform_inclusive_scan(std::execution::par, vector<double>) (cheap)/67108864/real_time 23117691 ns 20835083 ns 12
std::transform_inclusive_scan(std::execution::par, vector<double>) (expensive)/64/real_time 1470 ns 1465 ns 192185
std::transform_inclusive_scan(std::execution::par, vector<double>) (expensive)/65536/real_time 521367 ns 512009 ns 541
std::transform_inclusive_scan(std::execution::par, vector<double>) (expensive)/1048576/real_time 1427313 ns 1318365 ns 189
std::transform_inclusive_scan(std::execution::par, vector<double>) (expensive)/67108864/real_time 71749656 ns 59881750 ns 4
std::copy_if (libcxx/test/benchmarks/algorithms/modifying/pstl.copy_if.bench.cpp):
1-pass
std::copy_if(std::execution::par, vector<double>) (minimal)/64/real_time 1543 ns 1542 ns 175055 1.19x
std::copy_if(std::execution::par, vector<double>) (minimal)/65536/real_time 152085 ns 139928 ns 1853 1.24x
std::copy_if(std::execution::par, vector<double>) (minimal)/1048576/real_time 740612 ns 551550 ns 467 1.96x
std::copy_if(std::execution::par, vector<double>) (minimal)/67108864/real_time 19972413 ns 17199833 ns 12 1.12x
std::copy_if(std::execution::par, vector<double>) (cheap)/64/real_time 1596 ns 1595 ns 170396 1.17x
std::copy_if(std::execution::par, vector<double>) (cheap)/65536/real_time 154763 ns 151312 ns 1809 1.26x
std::copy_if(std::execution::par, vector<double>) (cheap)/1048576/real_time 461890 ns 429821 ns 593 2.02x
std::copy_if(std::execution::par, vector<double>) (cheap)/67108864/real_time 19696825 ns 16477200 ns 15 1.05x
std::copy_if(std::execution::par, vector<double>) (expensive)/64/real_time 1752 ns 1751 ns 153632 1.12x
std::copy_if(std::execution::par, vector<double>) (expensive)/65536/real_time 160312 ns 155406 ns 1784 1.25x
std::copy_if(std::execution::par, vector<double>) (expensive)/1048576/real_time 487009 ns 451649 ns 562 1.80x
std::copy_if(std::execution::par, vector<double>) (expensive)/67108864/real_time 18559486 ns 16851867 ns 15 1.16x
3-pass
std::copy_if(std::execution::par, vector<double>) (minimal)/64/real_time 1843 ns 1842 ns 147303
std::copy_if(std::execution::par, vector<double>) (minimal)/65536/real_time 189064 ns 186726 ns 1477
std::copy_if(std::execution::par, vector<double>) (minimal)/1048576/real_time 1457788 ns 941429 ns 280
std::copy_if(std::execution::par, vector<double>) (minimal)/67108864/real_time 22398447 ns 17145364 ns 11
std::copy_if(std::execution::par, vector<double>) (cheap)/64/real_time 1871 ns 1869 ns 152447
std::copy_if(std::execution::par, vector<double>) (cheap)/65536/real_time 195420 ns 192982 ns 1343
std::copy_if(std::execution::par, vector<double>) (cheap)/1048576/real_time 934680 ns 900108 ns 296
std::copy_if(std::execution::par, vector<double>) (cheap)/67108864/real_time 20809951 ns 14340167 ns 12
std::copy_if(std::execution::par, vector<double>) (expensive)/64/real_time 1973 ns 1967 ns 142532
std::copy_if(std::execution::par, vector<double>) (expensive)/65536/real_time 201738 ns 198699 ns 1345
std::copy_if(std::execution::par, vector<double>) (expensive)/1048576/real_time 879643 ns 846429 ns 333
std::copy_if(std::execution::par, vector<double>) (expensive)/67108864/real_time 21548817 ns 12768231 ns 13
```
On my hardware (AppleM5P) the single-pass approach is ~1.1-1.2x faster on average, though it's very dependent on the operations and the partition sizes.
https://github.com/llvm/llvm-project/pull/236219
More information about the libcxx-commits
mailing list