Reputation: 63
I am trying to parallelize quicksort using std::threads
, and I am receiving an error I am unfamiliar with since I just started multithreading. The error is probably so simple, I keep skipping over it. Can someone please shed some light on the problem. Here is the code and the only error that appears:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream> //cout, endl
#include <cstdlib> //srand
#include <algorithm>//copy, random_shuffle
#include <iterator> //ostream_iterator
#include "ratio.h"
#include <vector>
#include <iostream>
#include <thread>
#include "quicksort.h"
#include "sort_small_arrays.h"
template< typename T>
unsigned partition(T* a, unsigned begin, unsigned end) {
unsigned i = begin, last = end - 1;
T pivot = a[last];
for (unsigned j = begin; j<last; ++j) {
if (a[j]<pivot) {
std::swap(a[j], a[i]);
++i;
}
}
std::swap(a[i], a[last]);
return i;
}
/* iterative */
#define STACK
#define xVECTOR
#define xPRIORITY_QUEUE
#include <utility> // std::pair
template <typename T>
using triple = typename std::pair< T*, std::pair<unsigned, unsigned>>;
template< typename T>
struct compare_triples {
bool operator() (triple<T> const& op1, triple<T> const& op2) const {
return op1.second.first > op2.second.first;
}
};
#ifdef STACK
#include <stack>
template< typename T>
using Container = std::stack< triple<T>>;
#define PUSH push
#define TOP top
#define POP pop
#endif
#ifdef VECTOR
#include <vector>
template< typename T>
using Container = std::vector< triple<T>>;
#define PUSH push_back
#define TOP back
#define POP pop_back
#endif
#ifdef PRIORITY_QUEUE
#include <queue>
template< typename T>
using Container = std::priority_queue< triple<T>, std::vector<triple<T>>, compare_triples<T> >;
#define PUSH push
#define TOP top
#define POP pop
#endif
//Thread quicksorts a single range of elements and decrements thread counter at the end
template< typename T>
void threadsort_iterative_aux(Container<T> & ranges, int ¤tThreads)
{
triple<T> r = ranges.TOP();
ranges.POP();
T* a = r.first;
unsigned b = r.second.first;
unsigned e = r.second.second;
//base case
if (e - b<6) {
switch (e - b) {
case 5: quicksort_base_5(a + b); break;
case 4: quicksort_base_4(a + b); break;
case 3: quicksort_base_3(a + b); break;
case 2: quicksort_base_2(a + b); break;
}
continue;
}
unsigned q = partition(a, b, e);
ranges.PUSH(std::make_pair(a, std::make_pair(b, q)));
ranges.PUSH(std::make_pair(a, std::make_pair(q + 1, e)));
--currentThreads;
}
template< typename T>
void quicksort(T* a, unsigned begin, unsigned end, int num_threads)
{
//Number of threads currently running other than the main thread
int currentThreads = 0;
//Ranges of elements to sort
Container<T> ranges;
ranges.PUSH(std::make_pair(a, std::make_pair(begin, end)));
//Dynamic vector of threads
std::vector<std::thread> threads;
//Loops till all threads are done AND nothing left to sort
while (!ranges.empty() || currentThreads != 0)
{
//Doesn't bother doing anything if the range is empty but other threads are still running
if (!ranges.empty())
{
//If we can make more threads, make a thread and give it the top range to sort
if (currentThreads < num_threads)
{
++currentThreads;
threads.push_back(std::thread(threadsort_iterative_aux, std::ref(ranges), std::ref(currentThreads)));
}
//Starts sorting itself if maximum number of threads are running
else
{
triple<T> r = ranges.TOP();
ranges.POP();
T* a = r.first;
unsigned b = r.second.first;
unsigned e = r.second.second;
//Optimized sorting of a range between 2 and 5 elements
if (e - b < 6) {
switch (e - b) {
case 5: quicksort_base_5(a + b); break;
case 4: quicksort_base_4(a + b); break;
case 3: quicksort_base_3(a + b); break;
case 2: quicksort_base_2(a + b); break;
}
continue;
}
unsigned q = partition(a, b, e);
ranges.PUSH(std::make_pair(a, std::make_pair(b, q)));
ranges.PUSH(std::make_pair(a, std::make_pair(q + 1, e)));
}
}
}
}
template< typename T>
bool check_is_sorted(T* a, unsigned size)
{
for (unsigned int i = 1; i<size; ++i) {
if (!(a[i - 1] <= a[i])) {
return false;
}
}
return true;
}
bool test_int(unsigned size, unsigned num_threads) {
int* a = new int[size];
for (unsigned i = 0; i<size; ++i) { a[i] = i; }
std::srand(static_cast<unsigned int>(std::time(NULL)));
std::random_shuffle(a, a + size);
quicksort(a, 0, size, num_threads);
bool retval = check_is_sorted(a, size);
delete[] a;
return retval;
}
void test0() {
if (test_int(200, 1)) { std::cout << "OK\n"; }
else { std::cout << "Failed\n"; }
}
#include <cstdio> /* sscanf */
int main(int argc, char ** argv)
{
test0();
return 0;
}
Severity Code Description Project File Line Error C2440 '': cannot convert from 'initializer list' to 'std::thread' Line 145 (contains:
threads.push_back(std::thread(threadsort_iterative_aux, std::ref(ranges), std::ref(currentThreads)));)
Upvotes: 6
Views: 16080
Reputation: 54777
You pass a triple<T>
as the first parameter to threadsort_iterative_aux
when creating the thread, but the function expects a Container<T> &
there.
Also note that passing parameters by non-const-reference with this signature, you need to wrap the parameters in std::ref()
at the caller's side.
This is basically the same behavior as with std::bind
: If you omit the std::ref
, the compiler will copy the value into the binding and then pass that copy as a parameter into the function call upon invocation. Since the copy is immutable though, that will break with non-const references. Which is a good thing, as it protects you from accidentally losing the side-effects on that parameter.
Last but not least, the bind mechanism breaks template-argument deduction. Since you are not actually calling the function at the point where you create the thread, the compiler is unable to deduce the template parameter for the function from the arguments automatically. You have to give the parameter explicitly:
threads.push_back(std::thread(threadsort_iterative_aux<T>, std::ref(ranges), std::ref(currentThreads)));
// note the <T> here ---^
Since these are a mighty bunch of bind-headaches to worry about, you might want to just use a lambda instead, which suffers from none of those issues:
threads.push_back(std::thread([&]() { threadsort_iterative_aux(ranges, currentThreads); }));
Upvotes: 3