Skip to content

Commit

Permalink
verge_sort: avoid computing the collection size when passed (#169)
Browse files Browse the repository at this point in the history
  • Loading branch information
Morwenn committed Aug 22, 2020
1 parent 02a4c76 commit b0759f0
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 26 deletions.
4 changes: 2 additions & 2 deletions include/cpp-sort/adapters/verge_adapter.h
@@ -1,7 +1,7 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017-2019 Morwenn
* Copyright (c) 2017-2020 Morwenn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -74,7 +74,7 @@ namespace cppsort
"verge_adapter requires at least random-access iterators"
);

vergesort(std::move(first), std::move(last),
vergesort(std::move(first), std::move(last), last - first,
std::move(compare), std::move(projection),
this->get());
}
Expand Down
41 changes: 20 additions & 21 deletions include/cpp-sort/detail/vergesort.h
Expand Up @@ -45,29 +45,27 @@ namespace detail
{
template<typename BidirectionalIterator, typename Compare, typename Projection>
auto vergesort(BidirectionalIterator first, BidirectionalIterator last,
difference_type_t<BidirectionalIterator> size,
Compare compare, Projection projection,
std::bidirectional_iterator_tag)
-> void
{
using difference_type = difference_type_t<BidirectionalIterator>;
difference_type dist = std::distance(first, last);

if (dist < 80) {
if (size < 80) {
// vergesort is inefficient for small collections
quick_merge_sort(std::move(first), std::move(last), dist,
quick_merge_sort(std::move(first), std::move(last), size,
std::move(compare), std::move(projection));
return;
}

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

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

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

// Pair of iterators to iterate through the collection
BidirectionalIterator next = is_sorted_until(first, last, compare, projection);
Expand All @@ -87,8 +85,8 @@ namespace detail
}

// Reverse and merge
dist = std::distance(begin_rng, next);
if (dist > unstable_limit) {
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);
Expand All @@ -101,7 +99,7 @@ namespace detail
detail::inplace_merge(first, begin_rng, next, compare, projection);
}
} else {
size_unstable += dist;
size_unstable += size;
if (begin_unstable == last) {
begin_unstable = begin_rng;
}
Expand All @@ -122,8 +120,8 @@ namespace detail
}

// Merge
dist = std::distance(begin_rng, next);
if (dist > unstable_limit) {
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);
Expand All @@ -134,7 +132,7 @@ namespace detail
detail::inplace_merge(first, begin_rng, next, compare, projection);
}
} else {
size_unstable += dist;
size_unstable += size;
if (begin_unstable == last) {
begin_unstable = begin_rng;
}
Expand All @@ -156,22 +154,20 @@ namespace detail
template<typename RandomAccessIterator, typename Compare,
typename Projection, typename Fallback>
auto vergesort(RandomAccessIterator first, RandomAccessIterator last,
difference_type_t<RandomAccessIterator> size,
Compare compare, Projection projection, Fallback fallback,
std::random_access_iterator_tag)
-> void
{
using difference_type = difference_type_t<RandomAccessIterator>;
difference_type dist = last - first;

if (dist < 128) {
if (size < 128) {
// Vergesort is inefficient for small collections
fallback(std::move(first), std::move(last),
std::move(compare), std::move(projection));
return;
}

// Limit under which pdqsort is used to sort a sub-sequence
const difference_type unstable_limit = dist / log2(dist);
const difference_type_t<RandomAccessIterator> unstable_limit = size / log2(size);

// Vergesort detects big runs in ascending or descending order,
// and remember where each run ends by storing the end iterator
Expand Down Expand Up @@ -313,34 +309,37 @@ namespace detail

template<typename RandomAccessIterator, typename Compare, typename Projection>
auto vergesort(RandomAccessIterator first, RandomAccessIterator last,
difference_type_t<RandomAccessIterator> size,
Compare compare, Projection projection,
std::random_access_iterator_tag category)
-> void
{
using sorter = cppsort::pdq_sorter;
vergesort(std::move(first), std::move(last),
vergesort(std::move(first), std::move(last), size,
std::move(compare), std::move(projection),
sorter{}, category);
}

template<typename BidirectionalIterator, typename Compare,
typename Projection, typename Fallback>
auto vergesort(BidirectionalIterator first, BidirectionalIterator last,
difference_type_t<BidirectionalIterator> size,
Compare compare, Projection projection, Fallback fallback)
-> void
{
vergesort(std::move(first), std::move(last),
vergesort(std::move(first), std::move(last), size,
std::move(compare), std::move(projection),
std::move(fallback), std::random_access_iterator_tag{});
}

template<typename BidirectionalIterator, typename Compare, typename Projection>
auto vergesort(BidirectionalIterator first, BidirectionalIterator last,
difference_type_t<BidirectionalIterator> size,
Compare compare, Projection projection)
-> void
{
using category = iterator_category_t<BidirectionalIterator>;
vergesort(std::move(first), std::move(last),
vergesort(std::move(first), std::move(last), size,
std::move(compare), std::move(projection),
category{});
}
Expand Down
33 changes: 30 additions & 3 deletions include/cpp-sort/sorters/verge_sorter.h
@@ -1,7 +1,7 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015-2016 Morwenn
* Copyright (c) 2015-2020 Morwenn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -34,6 +34,7 @@
#include <cpp-sort/sorter_facade.h>
#include <cpp-sort/sorter_traits.h>
#include <cpp-sort/utility/functional.h>
#include <cpp-sort/utility/size.h>
#include <cpp-sort/utility/static_const.h>
#include "../detail/iterator_traits.h"
#include "../detail/vergesort.h"
Expand All @@ -47,6 +48,31 @@ namespace cppsort
{
struct verge_sorter_impl
{
template<
typename BidirectionalIterable,
typename Compare = std::less<>,
typename Projection = utility::identity,
typename = std::enable_if_t<
is_projection_v<Projection, BidirectionalIterable, Compare>
>
>
auto operator()(BidirectionalIterable&& iterable,
Compare compare={}, Projection projection={}) const
-> void
{
static_assert(
std::is_base_of<
std::forward_iterator_tag,
iterator_category_t<decltype(std::begin(iterable))>
>::value,
"verge_sorter requires at least bidirectional iterators"
);

vergesort(std::begin(iterable), std::end(iterable),
utility::size(iterable),
std::move(compare), std::move(projection));
}

template<
typename BidirectionalIterator,
typename Compare = std::less<>,
Expand All @@ -61,13 +87,14 @@ namespace cppsort
{
static_assert(
std::is_base_of<
std::bidirectional_iterator_tag,
std::forward_iterator_tag,
iterator_category_t<BidirectionalIterator>
>::value,
"verge_sorter requires at least bidirectional iterators"
);

vergesort(std::move(first), std::move(last),
auto size = std::distance(first, last);
vergesort(std::move(first), std::move(last), size,
std::move(compare), std::move(projection));
}

Expand Down

0 comments on commit b0759f0

Please sign in to comment.