xref: /oneTBB/test/tbb/test_parallel_sort.cpp (revision b15aabb3)
151c0b2f7Stbbdev /*
2*b15aabb3Stbbdev     Copyright (c) 2005-2021 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1751c0b2f7Stbbdev #include "common/test.h"
1851c0b2f7Stbbdev #include "common/utils_concurrency_limit.h"
1951c0b2f7Stbbdev #include "common/cpu_usertime.h"
2051c0b2f7Stbbdev 
2151c0b2f7Stbbdev #include "tbb/parallel_sort.h"
2251c0b2f7Stbbdev #include "tbb/concurrent_vector.h"
2351c0b2f7Stbbdev #include "tbb/global_control.h"
2451c0b2f7Stbbdev 
2551c0b2f7Stbbdev #include <math.h>
2651c0b2f7Stbbdev #include <vector>
2751c0b2f7Stbbdev #include <functional>
2851c0b2f7Stbbdev #include <string>
2951c0b2f7Stbbdev #include <cstring>
3051c0b2f7Stbbdev #include <cstddef>
3151c0b2f7Stbbdev #include <cstdio>
3251c0b2f7Stbbdev 
3351c0b2f7Stbbdev //! \file test_parallel_sort.cpp
3451c0b2f7Stbbdev //! \brief Test for [algorithms.parallel_sort]
3551c0b2f7Stbbdev 
3651c0b2f7Stbbdev /** Has tightly controlled interface so that we can verify
3751c0b2f7Stbbdev     that parallel_sort uses only the required interface. */
3851c0b2f7Stbbdev class Minimal {
3951c0b2f7Stbbdev     int val;
4051c0b2f7Stbbdev public:
4151c0b2f7Stbbdev     void set_val(int i) { val = i; }
4251c0b2f7Stbbdev     static bool Less (const Minimal &a, const Minimal &b) {
4351c0b2f7Stbbdev         return (a.val < b.val);
4451c0b2f7Stbbdev     }
4551c0b2f7Stbbdev     static bool AreEqual( Minimal &a,  Minimal &b) {
4651c0b2f7Stbbdev        return a.val == b.val;
4751c0b2f7Stbbdev     }
4851c0b2f7Stbbdev };
4951c0b2f7Stbbdev 
5051c0b2f7Stbbdev //! Defines a comparison function object for Minimal
5151c0b2f7Stbbdev class MinimalLessCompare {
5251c0b2f7Stbbdev public:
5351c0b2f7Stbbdev     bool operator() (const Minimal &a, const Minimal &b) const {
5451c0b2f7Stbbdev         return Minimal::Less(a,b);
5551c0b2f7Stbbdev     }
5651c0b2f7Stbbdev };
5751c0b2f7Stbbdev 
5851c0b2f7Stbbdev //! The default validate; but it uses operator== which is not required
5951c0b2f7Stbbdev template<typename Range>
6051c0b2f7Stbbdev void validate(Range test_range, Range sorted_range, std::size_t size) {
6151c0b2f7Stbbdev     for (std::size_t i = 0; i < size; i++) {
6251c0b2f7Stbbdev         REQUIRE( test_range[i] == sorted_range[i] );
6351c0b2f7Stbbdev     }
6451c0b2f7Stbbdev }
6551c0b2f7Stbbdev 
6651c0b2f7Stbbdev //! A validate() specialized to Minimal since it does not define an operator==
6751c0b2f7Stbbdev void validate(Minimal* test_range, Minimal* sorted_range, std::size_t size) {
6851c0b2f7Stbbdev     for (std::size_t i = 0; i < size; i++) {
6951c0b2f7Stbbdev         REQUIRE( Minimal::AreEqual(test_range[i], sorted_range[i]) );
7051c0b2f7Stbbdev     }
7151c0b2f7Stbbdev }
7251c0b2f7Stbbdev 
7351c0b2f7Stbbdev //! A validate() specialized to concurrent_vector<Minimal> since it does not define an operator==
7451c0b2f7Stbbdev void validate(tbb::concurrent_vector<Minimal>::iterator test_range, tbb::concurrent_vector<Minimal>::iterator sorted_range, std::size_t size) {
7551c0b2f7Stbbdev     for (std::size_t i = 0; i < size; i++) {
7651c0b2f7Stbbdev         REQUIRE( Minimal::AreEqual(test_range[i], sorted_range[i]) );
7751c0b2f7Stbbdev     }
7851c0b2f7Stbbdev }
7951c0b2f7Stbbdev 
8051c0b2f7Stbbdev //! The default initialization routine.
8151c0b2f7Stbbdev /*! This routine assumes that you can assign to the elements from a float.
8251c0b2f7Stbbdev     It assumes that iter and sorted_list have already been allocated. It fills
8351c0b2f7Stbbdev     them according to the current data set (tracked by a local static variable).
8451c0b2f7Stbbdev     Returns true if a valid test has been setup, or false if there is no test to
8551c0b2f7Stbbdev     perform.
8651c0b2f7Stbbdev */
8751c0b2f7Stbbdev template <typename RefType, typename ValueType>
8851c0b2f7Stbbdev void set(RefType& ref, ValueType new_value) {
8951c0b2f7Stbbdev     ref = static_cast<RefType>(new_value);
9051c0b2f7Stbbdev }
9151c0b2f7Stbbdev 
9251c0b2f7Stbbdev template <typename ValueType>
9351c0b2f7Stbbdev void set(Minimal& minimal_ref, ValueType new_value) {
9451c0b2f7Stbbdev     minimal_ref.set_val(static_cast<int>(new_value));
9551c0b2f7Stbbdev }
9651c0b2f7Stbbdev 
9751c0b2f7Stbbdev template <typename RandomAccessIterator, typename Compare>
9851c0b2f7Stbbdev bool fill_ranges(RandomAccessIterator test_range_begin, RandomAccessIterator sorted_range_begin,
9951c0b2f7Stbbdev     std::size_t size, const Compare &compare) {
10051c0b2f7Stbbdev 
10151c0b2f7Stbbdev     static char test_case = 0;
10251c0b2f7Stbbdev     const char num_cases = 3;
10351c0b2f7Stbbdev 
10451c0b2f7Stbbdev     if (test_case < num_cases) {
10551c0b2f7Stbbdev         // switch on the current test case, filling the test_list and sorted_list appropriately
10651c0b2f7Stbbdev         switch(test_case) {
10751c0b2f7Stbbdev             case 0:
10851c0b2f7Stbbdev                 /* use sin to generate the values */
10951c0b2f7Stbbdev                 for (std::size_t i = 0; i < size; i++) {
11051c0b2f7Stbbdev                     set(test_range_begin[i], sin(float(i)));
11151c0b2f7Stbbdev                     set(sorted_range_begin[i], sin(float(i)));
11251c0b2f7Stbbdev                 }
11351c0b2f7Stbbdev                 break;
11451c0b2f7Stbbdev             case 1:
11551c0b2f7Stbbdev                 /* presorted list */
11651c0b2f7Stbbdev                 for (std::size_t i = 0; i < size; i++) {
11751c0b2f7Stbbdev                     set(test_range_begin[i], i);
11851c0b2f7Stbbdev                     set(sorted_range_begin[i], i);
11951c0b2f7Stbbdev                 }
12051c0b2f7Stbbdev                 break;
12151c0b2f7Stbbdev             case 2:
12251c0b2f7Stbbdev                 /* reverse-sorted list */
12351c0b2f7Stbbdev                 for (std::size_t i = 0; i < size; i++) {
12451c0b2f7Stbbdev                     set(test_range_begin[i], size - i);
12551c0b2f7Stbbdev                     set(sorted_range_begin[i], size - i);
12651c0b2f7Stbbdev                 }
12751c0b2f7Stbbdev                 break;
12851c0b2f7Stbbdev         }
12951c0b2f7Stbbdev 
13051c0b2f7Stbbdev         // pre-sort sorted_range for later validity testing
13151c0b2f7Stbbdev         std::sort(sorted_range_begin, sorted_range_begin + size, compare);
13251c0b2f7Stbbdev         test_case++;
13351c0b2f7Stbbdev         return true;
13451c0b2f7Stbbdev     }
13551c0b2f7Stbbdev     test_case = 0;
13651c0b2f7Stbbdev     return false;
13751c0b2f7Stbbdev }
13851c0b2f7Stbbdev 
13951c0b2f7Stbbdev //! The initialization routine specialized to the class string
14051c0b2f7Stbbdev /*! strings are created from floats.
14151c0b2f7Stbbdev */
14251c0b2f7Stbbdev bool fill_ranges(std::string* iter, std::string* sorted_list, std::size_t size, const std::less<std::string> &compare) {
14351c0b2f7Stbbdev     static char test_case = 0;
14451c0b2f7Stbbdev     const char num_cases = 1;
14551c0b2f7Stbbdev 
14651c0b2f7Stbbdev     if (test_case < num_cases) {
14751c0b2f7Stbbdev         /* use sin to generate the values */
14851c0b2f7Stbbdev         for (std::size_t i = 0; i < size; i++) {
14951c0b2f7Stbbdev             char buffer[20];
15051c0b2f7Stbbdev // Getting rid of secure warning issued by VC 14 and newer
15151c0b2f7Stbbdev #if _MSC_VER && __STDC_SECURE_LIB__>=200411
15251c0b2f7Stbbdev             sprintf_s(buffer, sizeof(buffer), "%f", float(sin(float(i))));
15351c0b2f7Stbbdev #else
15451c0b2f7Stbbdev             sprintf(buffer, "%f", float(sin(float(i))));
15551c0b2f7Stbbdev #endif
15651c0b2f7Stbbdev             sorted_list[i] = iter[i] = std::string(buffer);
15751c0b2f7Stbbdev         }
15851c0b2f7Stbbdev 
15951c0b2f7Stbbdev         std::sort(sorted_list, sorted_list + size, compare);
16051c0b2f7Stbbdev         test_case++;
16151c0b2f7Stbbdev         return true;
16251c0b2f7Stbbdev     }
16351c0b2f7Stbbdev     test_case = 0;
16451c0b2f7Stbbdev     return false;
16551c0b2f7Stbbdev }
16651c0b2f7Stbbdev 
16751c0b2f7Stbbdev //! The default test routine.
16851c0b2f7Stbbdev /*! Tests all data set sizes from 0 to N, all grainsizes from 0 to G=10, and selects from
16951c0b2f7Stbbdev     all possible interfaces to parallel_sort depending on whether a scratch space and
17051c0b2f7Stbbdev     compare have been provided.
17151c0b2f7Stbbdev */
17251c0b2f7Stbbdev template<typename ValueType, std::size_t Size>
17351c0b2f7Stbbdev struct parallel_sort_test {
17451c0b2f7Stbbdev     static void run() {
17551c0b2f7Stbbdev          std::less<ValueType> default_comp;
17651c0b2f7Stbbdev          ValueType* array = new ValueType[Size];
17751c0b2f7Stbbdev          ValueType* sorted_array = new ValueType[Size];
17851c0b2f7Stbbdev 
17951c0b2f7Stbbdev          while (fill_ranges(array, sorted_array, Size, default_comp)) {
18051c0b2f7Stbbdev              tbb::parallel_sort(array, array + Size);
18151c0b2f7Stbbdev              validate(array, sorted_array, Size);
18251c0b2f7Stbbdev          }
18351c0b2f7Stbbdev 
18451c0b2f7Stbbdev          delete[] array;
18551c0b2f7Stbbdev          delete[] sorted_array;
18651c0b2f7Stbbdev     }
18751c0b2f7Stbbdev 
18851c0b2f7Stbbdev     template<typename Comparator>
18951c0b2f7Stbbdev     static void run(Comparator& comp) {
19051c0b2f7Stbbdev          ValueType* array = new ValueType[Size];
19151c0b2f7Stbbdev          ValueType* sorted_array = new ValueType[Size];
19251c0b2f7Stbbdev 
19351c0b2f7Stbbdev         while (fill_ranges(array, sorted_array, Size, comp)) {
19451c0b2f7Stbbdev             tbb::parallel_sort(array, array + Size, comp);
19551c0b2f7Stbbdev             validate(array, sorted_array, Size);
19651c0b2f7Stbbdev         }
19751c0b2f7Stbbdev 
19851c0b2f7Stbbdev         delete[] array;
19951c0b2f7Stbbdev         delete[] sorted_array;
20051c0b2f7Stbbdev     }
20151c0b2f7Stbbdev };
20251c0b2f7Stbbdev 
20351c0b2f7Stbbdev template<typename ValueType, std::size_t Size>
20451c0b2f7Stbbdev struct parallel_sort_test<tbb::concurrent_vector<ValueType>, Size> {
20551c0b2f7Stbbdev     static void run() {
20651c0b2f7Stbbdev         std::less<ValueType> default_comp;
20751c0b2f7Stbbdev         tbb::concurrent_vector<ValueType> vector(Size);
20851c0b2f7Stbbdev         tbb::concurrent_vector<ValueType> sorted_vector(Size);
20951c0b2f7Stbbdev 
21051c0b2f7Stbbdev         while (fill_ranges(std::begin(vector), std::begin(sorted_vector), Size, default_comp)) {
21151c0b2f7Stbbdev             tbb::parallel_sort(vector);
21251c0b2f7Stbbdev             validate(std::begin(vector), std::begin(sorted_vector), Size);
21351c0b2f7Stbbdev         }
21451c0b2f7Stbbdev     }
21551c0b2f7Stbbdev 
21651c0b2f7Stbbdev     template<typename Comparator>
21751c0b2f7Stbbdev     static void run(Comparator& comp) {
21851c0b2f7Stbbdev         tbb::concurrent_vector<ValueType> vector(Size);
21951c0b2f7Stbbdev         tbb::concurrent_vector<ValueType> sorted_vector(Size);
22051c0b2f7Stbbdev 
22151c0b2f7Stbbdev         while (fill_ranges(std::begin(vector), std::begin(sorted_vector), Size, comp)) {
22251c0b2f7Stbbdev             tbb::parallel_sort(vector, comp);
22351c0b2f7Stbbdev             validate(std::begin(vector), std::begin(sorted_vector), Size);
22451c0b2f7Stbbdev         }
22551c0b2f7Stbbdev     }
22651c0b2f7Stbbdev };
22751c0b2f7Stbbdev 
22851c0b2f7Stbbdev template<typename ValueType, typename Comparator>
22951c0b2f7Stbbdev void parallel_sort_test_suite() {
23051c0b2f7Stbbdev     Comparator comp;
23151c0b2f7Stbbdev     for (auto concurrency_level : utils::concurrency_range()) {
23251c0b2f7Stbbdev         tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
23351c0b2f7Stbbdev         parallel_sort_test<ValueType, /*Size*/0    >::run(comp);
23451c0b2f7Stbbdev         parallel_sort_test<ValueType, /*Size*/1    >::run(comp);
23551c0b2f7Stbbdev         parallel_sort_test<ValueType, /*Size*/10   >::run(comp);
23651c0b2f7Stbbdev         parallel_sort_test<ValueType, /*Size*/9999 >::run(comp);
23751c0b2f7Stbbdev         parallel_sort_test<ValueType, /*Size*/50000>::run(comp);
23851c0b2f7Stbbdev     }
23951c0b2f7Stbbdev }
24051c0b2f7Stbbdev 
24151c0b2f7Stbbdev template<typename ValueType>
24251c0b2f7Stbbdev void parallel_sort_test_suite() {
24351c0b2f7Stbbdev     for (auto concurrency_level : utils::concurrency_range()) {
24451c0b2f7Stbbdev         tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
24551c0b2f7Stbbdev         parallel_sort_test<ValueType, /*Size*/0    >::run();
24651c0b2f7Stbbdev         parallel_sort_test<ValueType, /*Size*/1    >::run();
24751c0b2f7Stbbdev         parallel_sort_test<ValueType, /*Size*/10   >::run();
24851c0b2f7Stbbdev         parallel_sort_test<ValueType, /*Size*/9999 >::run();
24951c0b2f7Stbbdev         parallel_sort_test<ValueType, /*Size*/50000>::run();
25051c0b2f7Stbbdev     }
25151c0b2f7Stbbdev }
25251c0b2f7Stbbdev 
25351c0b2f7Stbbdev //! Minimal array sorting test (less comparator)
25451c0b2f7Stbbdev //! \brief \ref error_guessing
25551c0b2f7Stbbdev TEST_CASE("Minimal array sorting test (less comparator)") {
25651c0b2f7Stbbdev     parallel_sort_test_suite<Minimal, MinimalLessCompare>();
25751c0b2f7Stbbdev }
25851c0b2f7Stbbdev 
25951c0b2f7Stbbdev //! Float array sorting test (default comparator)
26051c0b2f7Stbbdev //! \brief \ref error_guessing
26151c0b2f7Stbbdev TEST_CASE("Float array sorting test (default comparator)") {
26251c0b2f7Stbbdev     parallel_sort_test_suite<float>();
26351c0b2f7Stbbdev }
26451c0b2f7Stbbdev 
26551c0b2f7Stbbdev //! tbb::concurrent_vector<float> sorting test (less comparator)
26651c0b2f7Stbbdev //! \brief \ref error_guessing
26751c0b2f7Stbbdev TEST_CASE("tbb::concurrent_vector<float> sorting test (less comparator)") {
26851c0b2f7Stbbdev     parallel_sort_test_suite<tbb::concurrent_vector<float>, std::less<float>>();
26951c0b2f7Stbbdev }
27051c0b2f7Stbbdev 
27151c0b2f7Stbbdev //! tbb::concurrent_vector<float> sorting test (default comparator)
27251c0b2f7Stbbdev //! \brief \ref error_guessing
27351c0b2f7Stbbdev TEST_CASE("tbb::concurrent_vector<float> sorting test (default comparator)") {
27451c0b2f7Stbbdev     parallel_sort_test_suite<tbb::concurrent_vector<float>>();
27551c0b2f7Stbbdev }
27651c0b2f7Stbbdev 
27751c0b2f7Stbbdev //! Array of strings sorting test (less comparator)
27851c0b2f7Stbbdev //! \brief \ref error_guessing
27951c0b2f7Stbbdev TEST_CASE("Array of strings sorting test (less comparator)") {
28051c0b2f7Stbbdev     parallel_sort_test_suite<std::string, std::less<std::string>>();
28151c0b2f7Stbbdev }
28251c0b2f7Stbbdev 
28351c0b2f7Stbbdev //! Array of strings sorting test (default comparator)
28451c0b2f7Stbbdev //! \brief \ref error_guessing
28551c0b2f7Stbbdev TEST_CASE("Array of strings sorting test (default comparator)") {
28651c0b2f7Stbbdev     parallel_sort_test_suite<std::string>();
28751c0b2f7Stbbdev }
28851c0b2f7Stbbdev 
28951c0b2f7Stbbdev //! tbb::concurrent_vector<Minimal> sorting test (less comparator)
29051c0b2f7Stbbdev //! \brief \ref error_guessing
29151c0b2f7Stbbdev TEST_CASE("tbb::concurrent_vector<Minimal> sorting test (less comparator)") {
29251c0b2f7Stbbdev     parallel_sort_test_suite<tbb::concurrent_vector<Minimal>, MinimalLessCompare>();
29351c0b2f7Stbbdev }
29451c0b2f7Stbbdev 
29551c0b2f7Stbbdev const int vector_size = 10000;
29651c0b2f7Stbbdev 
29751c0b2f7Stbbdev //! Array sorting test (default comparator)
29851c0b2f7Stbbdev //! \brief \ref error_guessing
29951c0b2f7Stbbdev TEST_CASE("Array sorting test (default comparator)") {
30051c0b2f7Stbbdev     for ( auto concurrency_level : utils::concurrency_range() ) {
30151c0b2f7Stbbdev         tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
30251c0b2f7Stbbdev 
30351c0b2f7Stbbdev         int test_array[vector_size];
30451c0b2f7Stbbdev         for (int i = 0; i < vector_size; ++i)
30551c0b2f7Stbbdev             test_array[i] = rand() % vector_size;
30651c0b2f7Stbbdev 
30751c0b2f7Stbbdev         tbb::parallel_sort(test_array);
30851c0b2f7Stbbdev 
30951c0b2f7Stbbdev         for(int i = 0; i < vector_size - 1; ++i)
31051c0b2f7Stbbdev             REQUIRE_MESSAGE(test_array[i] <= test_array[i + 1], "Testing data not sorted");
31151c0b2f7Stbbdev     }
31251c0b2f7Stbbdev }
31351c0b2f7Stbbdev 
31451c0b2f7Stbbdev //! Testing workers going to sleep
31551c0b2f7Stbbdev //! \brief \ref resource_usage
31651c0b2f7Stbbdev TEST_CASE("That all workers sleep when no work") {
31751c0b2f7Stbbdev     int test_array[vector_size];
31851c0b2f7Stbbdev     for (int i = 0; i < vector_size; ++i)
31951c0b2f7Stbbdev         test_array[i] = rand() % vector_size;
32051c0b2f7Stbbdev 
32151c0b2f7Stbbdev     tbb::parallel_sort(test_array);
32251c0b2f7Stbbdev     TestCPUUserTime(utils::get_platform_max_threads());
32351c0b2f7Stbbdev }
324