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