Skip to content

Commit

Permalink
verge_sort: make better use of existing size information (#169)
Browse files Browse the repository at this point in the history
Add new innplace_merge overload for bidirectional iterators to allow to
pass the sizes of the subranges to merge without passing a buffer. Use
this information to avoid recomputing size information as often in the
vergesort overload that handles bidirectional iterators.

This commit also cleans the bidirectional overload of vergesort, which
had been kind of neglected.
  • Loading branch information
Morwenn committed Aug 30, 2020
1 parent bc8065f commit 3149c86
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 64 deletions.
33 changes: 33 additions & 0 deletions include/cpp-sort/detail/inplace_merge.h
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,25 @@ namespace detail
std::move(compare), std::move(projection));
}

template<typename BidirectionalIterator, typename Compare, typename Projection>
auto inplace_merge(BidirectionalIterator first, BidirectionalIterator middle,
BidirectionalIterator last,
Compare compare, Projection projection,
difference_type_t<BidirectionalIterator> len1,
difference_type_t<BidirectionalIterator> len2,
std::bidirectional_iterator_tag)
-> void
{
using rvalue_reference = remove_cvref_t<rvalue_reference_t<BidirectionalIterator>>;
temporary_buffer<rvalue_reference> buffer(std::min(len1, len2));

using category = iterator_category_t<BidirectionalIterator>;
inplace_merge(std::move(first), std::move(middle), std::move(last),
std::move(compare), std::move(projection),
len1, len2, buffer.data(), buffer.size(),
category{});
}

template<typename BidirectionalIterator, typename Compare, typename Projection>
auto inplace_merge(BidirectionalIterator first, BidirectionalIterator middle,
BidirectionalIterator last, Compare compare, Projection projection,
Expand Down Expand Up @@ -545,6 +564,20 @@ namespace detail
category{});
}

template<typename BidirectionalIterator, typename Compare, typename Projection>
auto inplace_merge(BidirectionalIterator first, BidirectionalIterator middle,
BidirectionalIterator last,
Compare compare, Projection projection,
difference_type_t<BidirectionalIterator> len1,
difference_type_t<BidirectionalIterator> len2)
-> void
{
using category = iterator_category_t<BidirectionalIterator>;
inplace_merge(std::move(first), std::move(middle), std::move(last),
std::move(compare), std::move(projection),
len1, len2, category{});
}

// Buffered overload, which also happens to take the length of the
// subranges, allowing not to cross the whole range to compute them
// from time to time
Expand Down
140 changes: 76 additions & 64 deletions include/cpp-sort/detail/vergesort.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,97 +57,111 @@ namespace detail
return;
}

using difference_type = difference_type_t<BidirectionalIterator>;
auto&& comp = utility::as_function(compare);
auto&& proj = utility::as_function(projection);

// Limit under which quick_merge_sort is used
int unstable_limit = size / log2(size);

// Beginning of an unstable partition, last if the
// previous partition is stable
BidirectionalIterator begin_unstable = last;
auto begin_unstable = last;

// Size of the unstable partition
difference_type_t<BidirectionalIterator> size_unstable = 0;
difference_type size_unstable = 0;

// Pair of iterators to iterate through the collection
BidirectionalIterator next = is_sorted_until(first, last, compare, projection);
BidirectionalIterator current = std::prev(next);

auto&& comp = utility::as_function(compare);
auto&& proj = utility::as_function(projection);
auto next = is_sorted_until(first, last, compare, projection);
auto current = std::prev(next);

while (true) {
BidirectionalIterator begin_rng = current;

// Decreasing range
while (next != last) {
if (comp(proj(*current), proj(*next))) break;
++current;
++next;
}
{
auto begin_rng = current;

// Reverse and merge
size = std::distance(begin_rng, next);
if (size > unstable_limit) {
if (begin_unstable != last) {
quick_merge_sort(begin_unstable, begin_rng, size_unstable, compare, projection);
detail::reverse(begin_rng, next);
detail::inplace_merge(begin_unstable, begin_rng, next, compare, projection);
detail::inplace_merge(first, begin_unstable, next, compare, projection);
begin_unstable = last;
size_unstable = 0;
} else {
detail::reverse(begin_rng, next);
detail::inplace_merge(first, begin_rng, next, compare, projection);
}
} else {
size_unstable += size;
if (begin_unstable == last) {
begin_unstable = begin_rng;
difference_type run_size = 1;
while (next != last) {
if (comp(proj(*current), proj(*next))) break;
++current;
++next;
++run_size;
}
}

if (next == last) break;

++current;
++next;
// Reverse and merge
if (run_size > unstable_limit) {
if (begin_unstable != last) {
quick_merge_sort(begin_unstable, begin_rng, size_unstable, compare, projection);
detail::reverse(begin_rng, next);
detail::inplace_merge(begin_unstable, begin_rng, next, compare, projection,
size_unstable, run_size);
detail::inplace_merge(first, begin_unstable, next, compare, projection,
std::distance(first, begin_unstable), size_unstable + run_size);
begin_unstable = last;
size_unstable = 0;
} else {
detail::reverse(begin_rng, next);
detail::inplace_merge(first, begin_rng, next, compare, projection,
std::distance(first, begin_rng), run_size);
}
} else {
size_unstable += run_size;
if (begin_unstable == last) {
begin_unstable = begin_rng;
}
}

begin_rng = current;
if (next == last) break;

// Increasing range
while (next != last) {
if (comp(proj(*next), proj(*current))) break;
++current;
++next;
}

// Merge
size = std::distance(begin_rng, next);
if (size > unstable_limit) {
if (begin_unstable != last) {
quick_merge_sort(begin_unstable, begin_rng, size_unstable, compare, projection);
detail::inplace_merge(begin_unstable, begin_rng, next, compare, projection);
detail::inplace_merge(first, begin_unstable, next, compare, projection);
begin_unstable = last;
size_unstable = 0;
} else {
detail::inplace_merge(first, begin_rng, next, compare, projection);
// Increasing range
{
auto begin_rng = current;

difference_type run_size = 1;
while (next != last) {
if (comp(proj(*next), proj(*current))) break;
++current;
++next;
++run_size;
}
} else {
size_unstable += size;
if (begin_unstable == last) {
begin_unstable = begin_rng;

// Merge
if (run_size > unstable_limit) {
if (begin_unstable != last) {
quick_merge_sort(begin_unstable, begin_rng, size_unstable, compare, projection);
detail::inplace_merge(begin_unstable, begin_rng, next, compare, projection,
size_unstable, run_size);
detail::inplace_merge(first, begin_unstable, next, compare, projection,
std::distance(first, begin_unstable), size_unstable + run_size);
begin_unstable = last;
size_unstable = 0;
} else {
detail::inplace_merge(first, begin_rng, next, compare, projection,
std::distance(first, begin_rng), run_size);
}
} else {
size_unstable += run_size;
if (begin_unstable == last) {
begin_unstable = begin_rng;
}
}
}

if (next == last) break;
if (next == last) break;

++current;
++next;
++current;
++next;
}
}

if (begin_unstable != last) {
quick_merge_sort(begin_unstable, last, size_unstable, compare, projection);
detail::inplace_merge(first, begin_unstable, last,
std::move(compare), std::move(projection));
std::move(compare), std::move(projection),
std::distance(first, begin_unstable), size_unstable);
}
}

Expand Down Expand Up @@ -297,9 +311,7 @@ namespace detail
do {
auto begin = first;
for (auto it = runs.begin() ; it != runs.end() && it != std::prev(runs.end()) ; ++it) {
detail::inplace_merge(begin, *it, *std::next(it),
compare, projection);

detail::inplace_merge(begin, *it, *std::next(it), compare, projection);
// Remove the middle iterator and advance
it = runs.erase(it);
begin = *it;
Expand Down

0 comments on commit 3149c86

Please sign in to comment.