From fe55e0fb19c3ba697b7aa862fe67d776fd293e8f Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Mon, 12 May 2025 10:40:58 -0400 Subject: [PATCH] Avoid unnecessary swaps for equal elements in __sift_down --- libcxx/include/__algorithm/sift_down.h | 4 +- .../sift_down_equal_elements.pass.cpp | 66 +++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 libcxx/test/libcxx/algorithms/alg.sorting/alg.heap.operations/make.heap/sift_down_equal_elements.pass.cpp diff --git a/libcxx/include/__algorithm/sift_down.h b/libcxx/include/__algorithm/sift_down.h index 42803e30631fb..088d5e34c374b 100644 --- a/libcxx/include/__algorithm/sift_down.h +++ b/libcxx/include/__algorithm/sift_down.h @@ -51,7 +51,7 @@ __sift_down(_RandomAccessIterator __first, } // check if we are in heap-order - if (__comp(*__child_i, *__start)) + if (!__comp(*__start, *__child_i)) // we are, __start is larger than its largest child return; @@ -75,7 +75,7 @@ __sift_down(_RandomAccessIterator __first, } // check if we are in heap-order - } while (!__comp(*__child_i, __top)); + } while (__comp(__top, *__child_i)); *__start = std::move(__top); } diff --git a/libcxx/test/libcxx/algorithms/alg.sorting/alg.heap.operations/make.heap/sift_down_equal_elements.pass.cpp b/libcxx/test/libcxx/algorithms/alg.sorting/alg.heap.operations/make.heap/sift_down_equal_elements.pass.cpp new file mode 100644 index 0000000000000..83d3c0bd6813f --- /dev/null +++ b/libcxx/test/libcxx/algorithms/alg.sorting/alg.heap.operations/make.heap/sift_down_equal_elements.pass.cpp @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03 + +// + +// template +// void make_heap(Iter first, Iter last); + +// This test ensures that equivalent elements are not moved or copied when the heap is created. + +#include +#include +#include +#include + +struct Stats { + int compared = 0; + int copied = 0; + int moved = 0; +} stats; + +struct MyPair { + std::pair p; + MyPair(int a, int b) : p{a, b} {} + MyPair(const MyPair& other) : p(other.p) { ++stats.copied; } + MyPair(MyPair&& other) : p(other.p) { ++stats.moved; } + MyPair& operator=(const MyPair& other) { + p = other.p; + ++stats.copied; + return *this; + } + MyPair& operator=(MyPair&& other) { + p = other.p; + ++stats.moved; + return *this; + } + friend bool operator<(const MyPair& lhs, const MyPair& rhs) { + ++stats.compared; + return lhs.p.first < rhs.p.first; + } + friend bool operator==(const MyPair& lhs, const MyPair& rhs) { return lhs.p == rhs.p; } +}; + +int main(int, char**) { + std::vector hp{{42, 1}, {42, 2}, {42, 3}, {42, 4}, {42, 5}, {42, 6}}; + std::vector original_hp = hp; + + stats = {}; + std::make_heap(hp.begin(), hp.end()); + + assert(stats.copied == 0); + assert(stats.moved == 0); + assert(stats.compared == static_cast(hp.size()) - 1); + + assert(hp == original_hp); + assert(std::is_heap(hp.begin(), hp.end())); + + return 0; +}