Skip to content

Commit

Permalink
Add adapted_pipm_inplace_merge test method
Browse files Browse the repository at this point in the history
  • Loading branch information
alugowski committed Jan 18, 2024
1 parent 9cf3f4f commit 0b888b0
Show file tree
Hide file tree
Showing 4 changed files with 641 additions and 4 deletions.
14 changes: 11 additions & 3 deletions benchmark/algorithm_bench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "utils.hpp"
#include <poolstl/algorithm>
#include "../tests/thirdparty/pdqsort.h"
#include "../tests/inplace_merge_without_buffer.hpp"

////////////////////////////////

Expand Down Expand Up @@ -95,6 +96,7 @@ BENCHMARK(for_each<std_par>)->Name("for_each(std::execution::par)")->UseRealTime
template <class ExecPolicy>
void sort(benchmark::State& state) {
auto source = random_vector<int>(arr_length / 10);
// auto source = random_vector<int>(arr_length);

for ([[maybe_unused]] auto _ : state) {
state.PauseTiming();
Expand All @@ -119,23 +121,29 @@ BENCHMARK(sort<std_par>)->Name("sort(std::execution::par)")->UseRealTime();

////////////////////////////////

template <class ExecPolicy>
template <class ExecPolicy, int which_impl>
void pluggable_sort_pdq(benchmark::State& state) {
auto source = random_vector<int>(arr_length / 10);
// auto source = random_vector<int>(arr_length);

for ([[maybe_unused]] auto _ : state) {
state.PauseTiming();
std::vector<int> values(source);
state.ResumeTiming();

poolstl::pluggable_sort(policy<ExecPolicy>::get(), values.begin(), values.end(), pdqsort);
if constexpr (which_impl == 1) {
poolstl::pluggable_sort(policy<ExecPolicy>::get(), values.begin(), values.end(), pdqsort);
} else if constexpr (which_impl == 2) {
poolstl::pluggable_sort(policy<ExecPolicy>::get(), values.begin(), values.end(), pdqsort, adapted_pipm_inplace_merge);
}

benchmark::DoNotOptimize(values);
benchmark::ClobberMemory();
}
}

BENCHMARK(pluggable_sort_pdq<poolstl_par>)->Name("pluggable_sort(poolstl::par, ..., pdqsort)")->UseRealTime();
BENCHMARK(pluggable_sort_pdq<poolstl_par, 1>)->Name("pluggable_sort(poolstl::par, ..., pdqsort)")->UseRealTime(); // uses pdqsort and std::inplace_merge (O(n) extra memory)
BENCHMARK(pluggable_sort_pdq<poolstl_par, 2>)->Name("pluggable_sort(poolstl::par, ..., pdqsort, pipm_merge)")->UseRealTime(); // uses pdqsort and adapted_pipm_inplace_merge (O(1) extra memory)

////////////////////////////////

Expand Down
39 changes: 39 additions & 0 deletions tests/inplace_merge_without_buffer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (C) 2023 Adam Lugowski. All rights reserved.
// Use of this source code is governed by:
// the BSD 2-clause license, the MIT license, or at your choosing the BSL-1.0 license found in the LICENSE.*.txt files.
// SPDX-License-Identifier: BSD-2-Clause OR MIT OR BSL-1.0

#ifndef POOLSTL_INPLACE_MERGE_WITHOUT_BUFFER_HPP
#define POOLSTL_INPLACE_MERGE_WITHOUT_BUFFER_HPP

#include "thirdparty/InplaceMerge.hh"

/**
* Like `std::inplace_merge` but uses a buffer-free algorithm described by Huang
* and Langston in "Practical In-Place Merging", Communications of the ACM, 1988, http://dx.doi.org/10.1145/42392.42403
* Generalizes the implementation by Keith Schwarz: http://keithschwarz.com/interesting/code/?dir=inplace-merge
*
* Schwarz's implementation only supports merging two ranges of equal size. This adapter also handles cases where the
* ranges are slightly different sizes, as happens when used as the merge step for a general-purpose sort.
* Drastically different sizes effectively fallback to std::inplace_merge, which is not buffer-free.
*/
template<class RandIt, class Compare>
void adapted_pipm_inplace_merge(RandIt first, RandIt mid, RandIt last, Compare comp) {
auto left_size = std::distance(first, mid);
auto right_size = std::distance(mid, last);

if (left_size == right_size) {
InplaceMerge(first, last, comp);
} else if (left_size < right_size) {
auto extra = right_size - left_size;
InplaceMerge(first, last - extra, comp);
std::inplace_merge(first, last - extra, last, comp);
} else {
// left_size > right_size
auto extra = left_size - right_size;
InplaceMerge(first + extra, last, comp);
std::inplace_merge(first, first + extra, last, comp);
}
}

#endif
39 changes: 38 additions & 1 deletion tests/poolstl_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

// for testing pluggable sort
#include "thirdparty/pdqsort.h"
#include "inplace_merge_without_buffer.hpp"

namespace ttp = task_thread_pool;
using poolstl::iota_iter;
Expand Down Expand Up @@ -284,6 +285,42 @@ TEST_CASE("for_each_chunk", "[alg][algorithm][poolstl]") {
}
}

TEST_CASE("inplace_merge", "[alg][algorithm]") {
std::vector<int> arr_sizes(150);
std::iota(arr_sizes.begin(), arr_sizes.end(), 1);

for (auto num_iters : arr_sizes) { //test_arr_sizes) {
for (int scramble_type = 0; scramble_type <= 2; ++scramble_type) {
auto source = iota_vector(num_iters);
std::vector<int> dest(source);
switch (scramble_type) {
case 0: std::reverse(source.begin(), source.end()); break;
case 1: scramble(source); break;
default: break;
}

std::vector<int> midpoints(num_iters);
std::iota(midpoints.begin(), midpoints.end(), 0);
for (auto mid : midpoints) {
std::sort(source.begin(), source.begin() + mid);
std::sort(source.begin() + mid, source.end());

// {
// std::vector<int> work(source);
// std::inplace_merge(work.begin(), work.begin() + mid, work.end(), std::less<int>());
// REQUIRE(work == dest);
// }

{
std::vector<int> work(source);
adapted_pipm_inplace_merge(work.begin(), work.begin() + mid, work.end(), std::less<int>());
REQUIRE(work == dest);
}
}
}
}
}

TEST_CASE("sort", "[alg][algorithm]") {
for (auto num_threads : test_thread_counts) {
ttp::task_thread_pool pool(num_threads);
Expand Down Expand Up @@ -319,7 +356,7 @@ TEST_CASE("sort", "[alg][algorithm]") {
poolstl::pluggable_sort(poolstl::par.on(pool), dest2.begin(), dest2.end(), std::less<int>(), pdqsort_branchless);
break;
case 5:
poolstl::pluggable_sort(poolstl::par.on(pool), dest2.begin(), dest2.end(), std::less<int>(), pdqsort_branchless, std::inplace_merge);
poolstl::pluggable_sort(poolstl::par.on(pool), dest2.begin(), dest2.end(), std::less<int>(), pdqsort_branchless, adapted_pipm_inplace_merge);
default:
break;
}
Expand Down
Loading

0 comments on commit 0b888b0

Please sign in to comment.