[libcxx-commits] [libcxx] [libc++] Tiny optimizations for is_permutation (PR #129565)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Wed Mar 19 11:31:21 PDT 2025


https://github.com/ldionne commented:

When I run the new benchmarks locally and compare before/after your patch, this is what I get:

<details>
<summary>Results</summary>

```
Comparing build/default/libcxx/test/benchmarks/algorithms/nonmodifying/Output/is_permutation.bench.cpp.dir/benchmark-result.json to build/candidate/libcxx/test/benchmarks/algorithms/nonmodifying/Output/is_permutation.bench.cpp.dir/benchmark-result.json
Benchmark                                                                            Time             CPU      Time Old      Time New       CPU Old       CPU New
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
std::is_permutation(vector<int>) (3leg) (common prefix)/8                         +0.0328         +0.0303             4             4             4             4
std::is_permutation(vector<int>) (3leg) (common prefix)/1024                      -0.1053         -0.1055           548           490           547           490
std::is_permutation(vector<int>) (3leg) (common prefix)/8192                      +0.0017         +0.0005          3925          3931          3918          3919
std::is_permutation(deque<int>) (3leg) (common prefix)/8                          +0.0026         +0.0026             7             7             7             7
std::is_permutation(deque<int>) (3leg) (common prefix)/1024                       +0.0149         +0.0105           657           667           657           664
std::is_permutation(deque<int>) (3leg) (common prefix)/8192                       +0.0104         +0.0070          5223          5277          5219          5256
std::is_permutation(list<int>) (3leg) (common prefix)/8                           +0.0475         +0.0485             5             5             5             5
std::is_permutation(list<int>) (3leg) (common prefix)/1024                        +0.4774         +0.4798          1095          1618          1092          1615
std::is_permutation(list<int>) (3leg) (common prefix)/8192                        +0.0071         +0.0071         13884         13983         13860         13959
std::is_permutation(vector<int>) (3leg, pred) (common prefix)/8                   +0.0399         +0.0389             4             4             4             4
std::is_permutation(vector<int>) (3leg, pred) (common prefix)/1024                -0.0139         -0.0111           461           454           459           454
std::is_permutation(vector<int>) (3leg, pred) (common prefix)/8192                +0.0212         +0.0181          3528          3603          3527          3591
std::is_permutation(deque<int>) (3leg, pred) (common prefix)/8                    +0.0034         +0.0032             8             8             8             8
std::is_permutation(deque<int>) (3leg, pred) (common prefix)/1024                 +0.0219         +0.0197           769           786           769           784
std::is_permutation(deque<int>) (3leg, pred) (common prefix)/8192                 +0.0147         +0.0138          6121          6211          6121          6205
std::is_permutation(list<int>) (3leg, pred) (common prefix)/8                     +0.0153         +0.0153             6             6             6             6
std::is_permutation(list<int>) (3leg, pred) (common prefix)/1024                  +0.0654         +0.0628          1111          1184          1111          1181
std::is_permutation(list<int>) (3leg, pred) (common prefix)/8192                  +0.3435         +0.3437         11485         15431         11483         15429
std::is_permutation(vector<int>) (4leg) (common prefix)/8                         +0.0604         +0.0598             6             7             6             7
std::is_permutation(vector<int>) (4leg) (common prefix)/1024                      +0.0248         +0.0243           572           586           572           586
std::is_permutation(vector<int>) (4leg) (common prefix)/8192                      +0.0091         +0.0091          4520          4561          4520          4561
std::is_permutation(deque<int>) (4leg) (common prefix)/8                          +0.0566         +0.0564            12            12            12            12
std::is_permutation(deque<int>) (4leg) (common prefix)/1024                       +0.0007         +0.0004           811           811           811           811
std::is_permutation(deque<int>) (4leg) (common prefix)/8192                       +0.0008         +0.0007          6440          6445          6439          6444
std::is_permutation(list<int>) (4leg) (common prefix)/8                           -0.0102         -0.0102             7             7             7             7
std::is_permutation(list<int>) (4leg) (common prefix)/1024                        +0.0079         +0.0079          1070          1079          1070          1078
std::is_permutation(list<int>) (4leg) (common prefix)/8192                        +0.0485         +0.0481         12583         13194         12583         13189
rng::is_permutation(vector<int>) (4leg) (common prefix)/8                         +0.1462         +0.1173             7             7             7             7
rng::is_permutation(vector<int>) (4leg) (common prefix)/1024                      +0.0298         +0.0246           583           600           583           597
rng::is_permutation(vector<int>) (4leg) (common prefix)/8192                      -0.0308         -0.0206          4738          4592          4684          4588
rng::is_permutation(deque<int>) (4leg) (common prefix)/8                          +0.2257         +0.2283            10            13            10            13
rng::is_permutation(deque<int>) (4leg) (common prefix)/1024                       -0.0090         -0.0062           825           817           821           816
rng::is_permutation(deque<int>) (4leg) (common prefix)/8192                       +0.0046         +0.0050          6465          6495          6454          6486
rng::is_permutation(list<int>) (4leg) (common prefix)/8                           +0.0054         +0.0048             7             7             7             7
rng::is_permutation(list<int>) (4leg) (common prefix)/1024                        +0.0921         +0.0527          1074          1173          1074          1130
rng::is_permutation(list<int>) (4leg) (common prefix)/8192                        -0.0457         -0.0489         13452         12837         13452         12795
std::is_permutation(vector<int>) (4leg, pred) (common prefix)/8                   +0.1036         +0.1025             6             7             6             7
std::is_permutation(vector<int>) (4leg, pred) (common prefix)/1024                -0.0533         -0.0534           607           574           607           574
std::is_permutation(vector<int>) (4leg, pred) (common prefix)/8192                -0.0726         -0.0705          4820          4470          4808          4469
std::is_permutation(deque<int>) (4leg, pred) (common prefix)/8                    +0.0179         +0.0174            11            12            11            12
std::is_permutation(deque<int>) (4leg, pred) (common prefix)/1024                 -0.0014         +0.0016           854           853           851           852
std::is_permutation(deque<int>) (4leg, pred) (common prefix)/8192                 +0.0032         +0.0044          6766          6788          6755          6785
std::is_permutation(list<int>) (4leg, pred) (common prefix)/8                     -0.0140         -0.0148             8             8             8             8
std::is_permutation(list<int>) (4leg, pred) (common prefix)/1024                  +0.0163         +0.0147          1179          1199          1179          1197
std::is_permutation(list<int>) (4leg, pred) (common prefix)/8192                  -0.0326         -0.0329         14402         13932         14400         13926
rng::is_permutation(vector<int>) (4leg, pred) (common prefix)/8                   +0.1177         +0.1137             6             7             6             7
rng::is_permutation(vector<int>) (4leg, pred) (common prefix)/1024                -0.0608         -0.0626           609           572           609           570
rng::is_permutation(vector<int>) (4leg, pred) (common prefix)/8192                -0.0641         -0.0643          4811          4503          4808          4499
rng::is_permutation(deque<int>) (4leg, pred) (common prefix)/8                    +0.0350         +0.0347            11            12            11            12
rng::is_permutation(deque<int>) (4leg, pred) (common prefix)/1024                 +0.0221         +0.0180           849           868           849           864
rng::is_permutation(deque<int>) (4leg, pred) (common prefix)/8192                 +0.0028         +0.0022          6775          6794          6775          6790
rng::is_permutation(list<int>) (4leg, pred) (common prefix)/8                     -0.0094         -0.0094             8             8             8             8
rng::is_permutation(list<int>) (4leg, pred) (common prefix)/1024                  -0.0004         -0.0003          1182          1182          1182          1182
rng::is_permutation(list<int>) (4leg, pred) (common prefix)/8192                  -0.1109         -0.1109         12824         11402         12823         11401
std::is_permutation(vector<int>) (3leg) (shuffled)/8                              +0.1544         +0.1540            49            56            49            56
std::is_permutation(vector<int>) (3leg) (shuffled)/1024                           -0.0041         -0.0042        417690        415998        417613        415878
std::is_permutation(deque<int>) (3leg) (shuffled)/8                               +0.0312         +0.0311            72            74            72            74
std::is_permutation(deque<int>) (3leg) (shuffled)/1024                            -0.0039         -0.0038        976937        973173        976905        973173
std::is_permutation(list<int>) (3leg) (shuffled)/8                                +0.0698         +0.0696            60            64            60            64
std::is_permutation(list<int>) (3leg) (shuffled)/1024                             +0.0014         +0.0008       2016154       2019032       2016155       2017847
std::is_permutation(vector<int>) (3leg, pred) (shuffled)/8                        +0.0055         +0.0066            62            62            61            62
std::is_permutation(vector<int>) (3leg, pred) (shuffled)/1024                     -0.0142         -0.0122       1070694       1055456       1068171       1055100
std::is_permutation(deque<int>) (3leg, pred) (shuffled)/8                         -0.0045         -0.0024            81            81            81            81
std::is_permutation(deque<int>) (3leg, pred) (shuffled)/1024                      -0.0031         -0.0026       1167616       1164036       1166812       1163794
std::is_permutation(list<int>) (3leg, pred) (shuffled)/8                          -0.1736         -0.1734            91            75            91            75
std::is_permutation(list<int>) (3leg, pred) (shuffled)/1024                       +0.0245         +0.0251       2229788       2284486       2228285       2284297
std::is_permutation(vector<int>) (4leg) (shuffled)/8                              +0.1556         +0.1562            49            57            49            57
std::is_permutation(vector<int>) (4leg) (shuffled)/1024                           -0.0037         -0.0024        413315        411780        412744        411745
std::is_permutation(deque<int>) (4leg) (shuffled)/8                               +0.0179         +0.0185            78            80            78            80
std::is_permutation(deque<int>) (4leg) (shuffled)/1024                            +0.0060         +0.0060        975699        981570        975697        981572
std::is_permutation(list<int>) (4leg) (shuffled)/8                                +0.0390         +0.0391            60            63            60            63
std::is_permutation(list<int>) (4leg) (shuffled)/1024                             -0.0042         -0.0034       2018535       2009991       2016729       2009779
rng::is_permutation(vector<int>) (4leg) (shuffled)/8                              +0.1454         +0.1470            49            56            49            56
rng::is_permutation(vector<int>) (4leg) (shuffled)/1024                           -0.0226         -0.0223        421068        411541        420894        411504
rng::is_permutation(deque<int>) (4leg) (shuffled)/8                               +0.0739         +0.0734            76            81            76            81
rng::is_permutation(deque<int>) (4leg) (shuffled)/1024                            -0.0013         -0.0017        991798        990556        990445        988751
rng::is_permutation(list<int>) (4leg) (shuffled)/8                                +0.0279         +0.0286            61            63            61            63
rng::is_permutation(list<int>) (4leg) (shuffled)/1024                             +0.0008         +0.0011       2013143       2014835       2011920       2014227
std::is_permutation(vector<int>) (4leg, pred) (shuffled)/8                        +0.0090         +0.0091            61            61            61            61
std::is_permutation(vector<int>) (4leg, pred) (shuffled)/1024                     -0.2018         -0.2012       1058702        845087       1057847        845031
std::is_permutation(deque<int>) (4leg, pred) (shuffled)/8                         +0.1386         +0.1383            81            92            81            92
std::is_permutation(deque<int>) (4leg, pred) (shuffled)/1024                      +0.0404         +0.0405       1164023       1211074       1163886       1211007
std::is_permutation(list<int>) (4leg, pred) (shuffled)/8                          -0.0203         -0.0199            77            75            77            75
std::is_permutation(list<int>) (4leg, pred) (shuffled)/1024                       -0.0073         -0.0065       2302394       2285626       2300674       2285627
rng::is_permutation(vector<int>) (4leg, pred) (shuffled)/8                        -0.0040         -0.0024            61            61            61            61
rng::is_permutation(vector<int>) (4leg, pred) (shuffled)/1024                     -0.1933         -0.1933       1047648        845138       1047652        845093
rng::is_permutation(deque<int>) (4leg, pred) (shuffled)/8                         +0.1698         +0.1701            79            92            79            92
rng::is_permutation(deque<int>) (4leg, pred) (shuffled)/1024                      +0.0406         +0.0407       1163094       1210313       1162968       1210314
rng::is_permutation(list<int>) (4leg, pred) (shuffled)/8                          -0.0023         -0.0031            76            76            76            76
rng::is_permutation(list<int>) (4leg, pred) (shuffled)/1024                       +0.0127         +0.0113       2294352       2323538       2292282       2318209
OVERALL_GEOMEAN                                                                   +0.0188         +0.0180             0             0             0             0
```
</details>

I think that's really interesting. Observations:
1. We're doing much worse with the new implementation on `std::list` and `std::deque`. I don't understand that, that needs investigation.
2. We're not doing better on `std::vector` like we would assume since `std::mismatch` is vectorized. The root cause here seems to be that we don't properly forward the knowledge that the predicate is `std::equal_to` to the call to `std::mismatch`. I think that might be due to the use of `reference_wrapper`, which might inhibit [this check](https://github.com/llvm/llvm-project/blob/ca0fe95a5481ca526375a1b439f6d3cc8600c085/libcxx/include/__algorithm/mismatch.h#L137). If that's the case, we could avoid using `std::ref` when we call `mismatch`, but we should probably fix the underlying issue by making sure that `__desugars_to<__equal_tag, ...>` understands when it gets passed a `reference_wrapper`. That seems like a general thing we should fix if it's broken, and that's actually the target of https://github.com/llvm/llvm-project/issues/129312.

I think those are two good directions for investigating, please let me know if you have questions!

https://github.com/llvm/llvm-project/pull/129565


More information about the libcxx-commits mailing list