blob: 58df9671a02f655ef55c50d8595a4d1cd8508929 [file] [log] [blame]
/*
Copyright 2005-2010 Intel Corporation. All Rights Reserved.
This file is part of Threading Building Blocks.
Threading Building Blocks is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
Threading Building Blocks is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Threading Building Blocks; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
As a special exception, you may use this file as part of a free software
library without restriction. Specifically, if other files instantiate
templates or use macros or inline functions from this file, or you compile
this file and link it with other files to produce an executable, this
file does not by itself cause the resulting executable to be covered by
the GNU General Public License. This exception does not however
invalidate any other reasons why the executable file might be covered by
the GNU General Public License.
*/
#include "tbb/parallel_sort.h"
#include "tbb/task_scheduler_init.h"
#include "tbb/concurrent_vector.h"
#include "harness.h"
#include <math.h>
#include <exception>
#if !TBB_USE_EXCEPTIONS && _MSC_VER
// Suppress "C++ exception handler used, but unwind semantics are not enabled" warning in STL headers
#pragma warning (push)
#pragma warning (disable: 4530)
#endif
#include <algorithm>
#include <iterator>
#include <functional>
#include <string>
#include <cstring>
#if !TBB_USE_EXCEPTIONS && _MSC_VER
#pragma warning (pop)
#endif
/** Has tightly controlled interface so that we can verify
that parallel_sort uses only the required interface. */
class Minimal {
int val;
public:
Minimal() {}
void set_val(int i) { val = i; }
static bool CompareWith (const Minimal &a, const Minimal &b) {
return (a.val < b.val);
}
static bool AreEqual( Minimal &a, Minimal &b) {
return a.val == b.val;
}
};
//! Defines a comparison function object for Minimal
class MinimalCompare {
public:
bool operator() (const Minimal &a, const Minimal &b) const {
return Minimal::CompareWith(a,b);
}
};
//! The default validate; but it uses operator== which is not required
template<typename RandomAccessIterator>
bool Validate(RandomAccessIterator a, RandomAccessIterator b, size_t n) {
for (size_t i = 0; i < n; i++) {
ASSERT( a[i] == b[i], NULL );
}
return true;
}
//! A Validate specialized to string for debugging-only
template<>
bool Validate<std::string *>(std::string * a, std::string * b, size_t n) {
for (size_t i = 0; i < n; i++) {
if ( Verbose && a[i] != b[i]) {
for (size_t j = 0; j < n; j++) {
REPORT("a[%llu] == %s and b[%llu] == %s\n", static_cast<unsigned long long>(j), a[j].c_str(), static_cast<unsigned long long>(j), b[j].c_str());
}
}
ASSERT( a[i] == b[i], NULL );
}
return true;
}
//! A Validate specialized to Minimal since it does not define an operator==
template<>
bool Validate<Minimal *>(Minimal *a, Minimal *b, size_t n) {
for (size_t i = 0; i < n; i++) {
ASSERT( Minimal::AreEqual(a[i],b[i]), NULL );
}
return true;
}
//! A Validate specialized to concurrent_vector<Minimal> since it does not define an operator==
template<>
bool Validate<tbb::concurrent_vector<Minimal>::iterator>(tbb::concurrent_vector<Minimal>::iterator a,
tbb::concurrent_vector<Minimal>::iterator b, size_t n) {
for (size_t i = 0; i < n; i++) {
ASSERT( Minimal::AreEqual(a[i],b[i]), NULL );
}
return true;
}
//! used in Verbose mode for identifying which data set is being used
static std::string test_type;
//! The default initialization routine.
/*! This routine assumes that you can assign to the elements from a float.
It assumes that iter and sorted_list have already been allocated. It fills
them according to the current data set (tracked by a local static variable).
Returns true if a valid test has been setup, or false if there is no test to
perform.
*/
template < typename RandomAccessIterator, typename Compare >
bool init_iter(RandomAccessIterator iter, RandomAccessIterator sorted_list, size_t n, const Compare &compare, bool reset) {
static char test_case = 0;
const char num_cases = 3;
if (reset) test_case = 0;
if (test_case < num_cases) {
// switch on the current test case, filling the iter and sorted_list appropriately
switch(test_case) {
case 0:
/* use sin to generate the values */
test_type = "sin";
for (size_t i = 0; i < n; i++)
iter[i] = sorted_list[i] = static_cast<typename std::iterator_traits< RandomAccessIterator >::value_type>(sin(float(i)));
break;
case 1:
/* presorted list */
test_type = "pre-sorted";
for (size_t i = 0; i < n; i++)
iter[i] = sorted_list[i] = static_cast<typename std::iterator_traits< RandomAccessIterator >::value_type>(i);
break;
case 2:
/* reverse-sorted list */
test_type = "reverse-sorted";
for (size_t i = 0; i < n; i++)
iter[i] = sorted_list[i] = static_cast<typename std::iterator_traits< RandomAccessIterator >::value_type>(n - i);
break;
}
// pre-sort sorted_list for later validity testing
std::sort(sorted_list, sorted_list + n, compare);
test_case++;
return true;
}
return false;
}
template < typename T, typename Compare >
bool init_iter(T * iter, T * sorted_list, size_t n, const Compare &compare, bool reset) {
static char test_case = 0;
const char num_cases = 3;
if (reset) test_case = 0;
if (test_case < num_cases) {
// switch on the current test case, filling the iter and sorted_list appropriately
switch(test_case) {
case 0:
/* use sin to generate the values */
test_type = "sin";
for (size_t i = 0; i < n; i++)
iter[i] = sorted_list[i] = T(sin(float(i)));
break;
case 1:
/* presorted list */
test_type = "pre-sorted";
for (size_t i = 0; i < n; i++)
iter[i] = sorted_list[i] = T(i);
break;
case 2:
/* reverse-sorted list */
test_type = "reverse-sorted";
for (size_t i = 0; i < n; i++)
iter[i] = sorted_list[i] = T(n - i);
break;
}
// pre-sort sorted_list for later validity testing
std::sort(sorted_list, sorted_list + n, compare);
test_case++;
return true;
}
return false;
}
//! The initialization routine specialized to the class Minimal
/*! Minimal cannot have floats assigned to it. This function uses the set_val method
*/
template < >
bool init_iter(Minimal* iter, Minimal * sorted_list, size_t n, const MinimalCompare &compare, bool reset) {
static char test_case = 0;
const char num_cases = 3;
if (reset) test_case = 0;
if (test_case < num_cases) {
switch(test_case) {
case 0:
/* use sin to generate the values */
test_type = "sin";
for (size_t i = 0; i < n; i++) {
iter[i].set_val( int( sin( float(i) ) * 1000.f) );
sorted_list[i].set_val( int ( sin( float(i) ) * 1000.f) );
}
break;
case 1:
/* presorted list */
test_type = "pre-sorted";
for (size_t i = 0; i < n; i++) {
iter[i].set_val( int(i) );
sorted_list[i].set_val( int(i) );
}
break;
case 2:
/* reverse-sorted list */
test_type = "reverse-sorted";
for (size_t i = 0; i < n; i++) {
iter[i].set_val( int(n-i) );
sorted_list[i].set_val( int(n-i) );
}
break;
}
std::sort(sorted_list, sorted_list + n, compare);
test_case++;
return true;
}
return false;
}
//! The initialization routine specialized to the class concurrent_vector<Minimal>
/*! Minimal cannot have floats assigned to it. This function uses the set_val method
*/
template < >
bool init_iter(tbb::concurrent_vector<Minimal>::iterator iter, tbb::concurrent_vector<Minimal>::iterator sorted_list,
size_t n, const MinimalCompare &compare, bool reset) {
static char test_case = 0;
const char num_cases = 3;
if (reset) test_case = 0;
if (test_case < num_cases) {
switch(test_case) {
case 0:
/* use sin to generate the values */
test_type = "sin";
for (size_t i = 0; i < n; i++) {
iter[i].set_val( int( sin( float(i) ) * 1000.f) );
sorted_list[i].set_val( int ( sin( float(i) ) * 1000.f) );
}
break;
case 1:
/* presorted list */
test_type = "pre-sorted";
for (size_t i = 0; i < n; i++) {
iter[i].set_val( int(i) );
sorted_list[i].set_val( int(i) );
}
break;
case 2:
/* reverse-sorted list */
test_type = "reverse-sorted";
for (size_t i = 0; i < n; i++) {
iter[i].set_val( int(n-i) );
sorted_list[i].set_val( int(n-i) );
}
break;
}
std::sort(sorted_list, sorted_list + n, compare);
test_case++;
return true;
}
return false;
}
//! The initialization routine specialized to the class string
/*! strings are created from floats.
*/
template<>
bool init_iter(std::string *iter, std::string *sorted_list, size_t n, const std::less<std::string> &compare, bool reset) {
static char test_case = 0;
const char num_cases = 1;
if (reset) test_case = 0;
if (test_case < num_cases) {
switch(test_case) {
case 0:
/* use sin to generate the values */
test_type = "sin";
for (size_t i = 0; i < n; i++) {
char buffer[20];
#if __STDC_SECURE_LIB__>=200411
sprintf_s(buffer, sizeof(buffer), "%f", float(sin(float(i))));
#else
sprintf(buffer, "%f", float(sin(float(i))));
#endif /* _MSC_VER>=1400 */
sorted_list[i] = iter[i] = std::string(buffer);
}
break;
}
std::sort(sorted_list, sorted_list + n, compare);
test_case++;
return true;
}
return false;
}
//! The current number of threads in use (for Verbose only)
static size_t current_p;
//! The current data type being sorted (for Verbose only)
static std::string current_type;
//! The default test routine.
/*! Tests all data set sizes from 0 to N, all grainsizes from 0 to G=10, and selects from
all possible interfaces to parallel_sort depending on whether a scratch space and
compare have been provided.
*/
template<typename RandomAccessIterator, typename Compare>
bool parallel_sortTest(size_t n, RandomAccessIterator iter, RandomAccessIterator sorted_list, const Compare *comp) {
bool passed = true;
Compare local_comp;
init_iter(iter, sorted_list, n, local_comp, true);
do {
REMARK("%s %s p=%llu n=%llu :",current_type.c_str(), test_type.c_str(),
static_cast<unsigned long long>(current_p), static_cast<unsigned long long>(n));
if (comp != NULL) {
tbb::parallel_sort(iter, iter + n, local_comp );
} else {
tbb::parallel_sort(iter, iter + n );
}
if (!Validate(iter, sorted_list, n))
passed = false;
REMARK("passed\n");
} while (init_iter(iter, sorted_list, n, local_comp, false));
return passed;
}
//! The test routine specialize to Minimal, since it does not have a less defined for it
template<>
bool parallel_sortTest(size_t n, Minimal * iter, Minimal * sorted_list, const MinimalCompare *compare) {
bool passed = true;
if (compare == NULL) return passed;
init_iter(iter, sorted_list, n, *compare, true);
do {
REMARK("%s %s p=%llu n=%llu :",current_type.c_str(), test_type.c_str(),
static_cast<unsigned long long>(current_p), static_cast<unsigned long long>(n));
tbb::parallel_sort(iter, iter + n, *compare );
if (!Validate(iter, sorted_list, n))
passed = false;
REMARK("passed\n");
} while (init_iter(iter, sorted_list, n, *compare, false));
return passed;
}
//! The test routine specialize to concurrent_vector of Minimal, since it does not have a less defined for it
template<>
bool parallel_sortTest(size_t n, tbb::concurrent_vector<Minimal>::iterator iter,
tbb::concurrent_vector<Minimal>::iterator sorted_list, const MinimalCompare *compare) {
bool passed = true;
if (compare == NULL) return passed;
init_iter(iter, sorted_list, n, *compare, true);
do {
REMARK("%s %s p=%llu n=%llu :",current_type.c_str(), test_type.c_str(),
static_cast<unsigned long long>(current_p), static_cast<unsigned long long>(n));
tbb::parallel_sort(iter, iter + n, *compare );
if (!Validate(iter, sorted_list, n))
passed = false;
REMARK("passed\n");
} while (init_iter(iter, sorted_list, n, *compare, false));
return passed;
}
//! The main driver for the tests.
/*! Minimal, float and string types are used. All interfaces to parallel_sort that are usable
by each type are tested.
*/
void Flog() {
// For each type create:
// the list to be sorted by parallel_sort (array)
// the list to be sort by STL sort (array_2)
// and a less function object
const size_t N = 50000;
Minimal *minimal_array = new Minimal[N];
Minimal *minimal_array_2 = new Minimal[N];
MinimalCompare minimal_less;
#if !__TBB_FLOATING_POINT_BROKEN
float *float_array = new float[N];
float *float_array_2 = new float[N];
std::less<float> float_less;
tbb::concurrent_vector<float> float_cv1;
tbb::concurrent_vector<float> float_cv2;
float_cv1.grow_to_at_least(N);
float_cv2.grow_to_at_least(N);
#endif /* !__TBB_FLOATING_POINT_BROKEN */
std::string *string_array = new std::string[N];
std::string *string_array_2 = new std::string[N];
std::less<std::string> string_less;
tbb::concurrent_vector<Minimal> minimal_cv1;
tbb::concurrent_vector<Minimal> minimal_cv2;
minimal_cv1.grow_to_at_least(N);
minimal_cv2.grow_to_at_least(N);
// run the appropriate tests for each type
current_type = "Minimal(less)";
parallel_sortTest(0, minimal_array, minimal_array_2, &minimal_less);
parallel_sortTest(1, minimal_array, minimal_array_2, &minimal_less);
parallel_sortTest(10, minimal_array, minimal_array_2, &minimal_less);
parallel_sortTest(9999, minimal_array, minimal_array_2, &minimal_less);
parallel_sortTest(50000, minimal_array, minimal_array_2, &minimal_less);
#if !__TBB_FLOATING_POINT_BROKEN
current_type = "float (no less)";
parallel_sortTest(0, float_array, float_array_2, static_cast<std::less<float> *>(NULL));
parallel_sortTest(1, float_array, float_array_2, static_cast<std::less<float> *>(NULL));
parallel_sortTest(10, float_array, float_array_2, static_cast<std::less<float> *>(NULL));
parallel_sortTest(9999, float_array, float_array_2, static_cast<std::less<float> *>(NULL));
parallel_sortTest(50000, float_array, float_array_2, static_cast<std::less<float> *>(NULL));
current_type = "float (less)";
parallel_sortTest(0, float_array, float_array_2, &float_less);
parallel_sortTest(1, float_array, float_array_2, &float_less);
parallel_sortTest(10, float_array, float_array_2, &float_less);
parallel_sortTest(9999, float_array, float_array_2, &float_less);
parallel_sortTest(50000, float_array, float_array_2, &float_less);
current_type = "concurrent_vector<float> (no less)";
parallel_sortTest(0, float_cv1.begin(), float_cv2.begin(), static_cast<std::less<float> *>(NULL));
parallel_sortTest(1, float_cv1.begin(), float_cv2.begin(), static_cast<std::less<float> *>(NULL));
parallel_sortTest(10, float_cv1.begin(), float_cv2.begin(), static_cast<std::less<float> *>(NULL));
parallel_sortTest(9999, float_cv1.begin(), float_cv2.begin(), static_cast<std::less<float> *>(NULL));
parallel_sortTest(50000, float_cv1.begin(), float_cv2.begin(), static_cast<std::less<float> *>(NULL));
current_type = "concurrent_vector<float> (less)";
parallel_sortTest(0, float_cv1.begin(), float_cv2.begin(), &float_less);
parallel_sortTest(1, float_cv1.begin(), float_cv2.begin(), &float_less);
parallel_sortTest(10, float_cv1.begin(), float_cv2.begin(), &float_less);
parallel_sortTest(9999, float_cv1.begin(), float_cv2.begin(), &float_less);
parallel_sortTest(50000, float_cv1.begin(), float_cv2.begin(), &float_less);
#endif /* !__TBB_FLOATING_POINT_BROKEN */
current_type = "string (no less)";
parallel_sortTest(0, string_array, string_array_2, static_cast<std::less<std::string> *>(NULL));
parallel_sortTest(1, string_array, string_array_2, static_cast<std::less<std::string> *>(NULL));
parallel_sortTest(10, string_array, string_array_2, static_cast<std::less<std::string> *>(NULL));
parallel_sortTest(9999, string_array, string_array_2, static_cast<std::less<std::string> *>(NULL));
parallel_sortTest(50000, string_array, string_array_2, static_cast<std::less<std::string> *>(NULL));
current_type = "string (less)";
parallel_sortTest(0, string_array, string_array_2, &string_less);
parallel_sortTest(1, string_array, string_array_2, &string_less);
parallel_sortTest(10, string_array, string_array_2, &string_less);
parallel_sortTest(9999, string_array, string_array_2, &string_less);
parallel_sortTest(50000, string_array, string_array_2, &string_less);
current_type = "concurrent_vector<Minimal> (less)";
parallel_sortTest(0, minimal_cv1.begin(), minimal_cv2.begin(), &minimal_less);
parallel_sortTest(1, minimal_cv1.begin(), minimal_cv2.begin(), &minimal_less);
parallel_sortTest(10, minimal_cv1.begin(), minimal_cv2.begin(), &minimal_less);
parallel_sortTest(9999, minimal_cv1.begin(), minimal_cv2.begin(), &minimal_less);
parallel_sortTest(50000, minimal_cv1.begin(), minimal_cv2.begin(), &minimal_less);
delete [] minimal_array;
delete [] minimal_array_2;
#if !__TBB_FLOATING_POINT_BROKEN
delete [] float_array;
delete [] float_array_2;
#endif /* !__TBB_FLOATING_POINT_BROKEN */
delete [] string_array;
delete [] string_array_2;
}
#include <cstdio>
#include "harness_cpu.h"
int TestMain () {
if( MinThread<1 ) {
REPORT("Usage: number of threads must be positive\n");
exit(1);
}
for( int p=MinThread; p<=MaxThread; ++p ) {
if( p>0 ) {
tbb::task_scheduler_init init( p );
current_p = p;
Flog();
// Test that all workers sleep when no work
TestCPUUserTime(p);
}
}
return Harness::Done;
}