Параллель для использования std :: thread?

Я новичок в std :: thread и пытаюсь кодировать parallel_for,
Я кодировал следующую вещь:

// parallel_for.cpp
// compilation: g++ -O3 -std=c++0x parallel_for.cpp -o parallel_for -lpthread
// execution: time ./parallel_for 100 50000000
// (100: number of threads, 50000000: vector size)
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <vector>
#include <thread>
#include <cmath>
#include <algorithm>
#include <numeric>
#include <utility>

// Parallel for
template<typename Iterator, class Function>
void parallel_for(const Iterator& first, const Iterator& last, Function&& f, const int nthreads = 1, const int threshold = 1000)
{
const unsigned int group = std::max(std::max(1, std::abs(threshold)), (last-first)/std::abs(nthreads));
std::vector<std::thread> threads;
for (Iterator it = first; it < last; it += group) {
threads.push_back(std::thread([=](){std::for_each(it, std::min(it+group, last), f);}));
}
std::for_each(threads.begin(), threads.end(), [=](std::thread& x){x.join();});
}

// Function to apply
template<typename Type>
void f1(Type& x)
{
x = std::sin(x)+std::exp(std::cos(x))/std::exp(std::sin(x));
}

// Main
int main(int argc, char* argv[]) {

const unsigned int nthreads = (argc > 1) ? std::atol(argv[1]) : (1);
const unsigned int n = (argc > 2) ? std::atol(argv[2]) : (100000000);
double x = 0;
std::vector<double> v(n);
std::iota(v.begin(), v.end(), 0);

parallel_for(v.begin(), v.end(), f1<double>, nthreads);

for (unsigned int i = 0; i < n; ++i) x += v[i];
std::cout<<std::setprecision(15)<<x<<std::endl;
return 0;
}

Но это не работает: (коды ошибок из g ++ 4.6)

parallel_for.cpp: In instantiation of ‘parallel_for(const Iterator&, const Iterator&, Function&&, int, int) [with Iterator = __gnu_cxx::__normal_iterator<double*, std::vector<double> >, Function = void (&)(double&)]::<lambda()>’:
parallel_for.cpp:22:9:   instantiated from ‘void parallel_for(const Iterator&, const Iterator&, Function&&, int, int) [with Iterator = __gnu_cxx::__normal_iterator<double*, std::vector<double> >, Function = void (&)(double&)]’
parallel_for.cpp:43:58:   instantiated from here
parallel_for.cpp:22:89: erreur: field ‘parallel_for(const Iterator&, const Iterator&, Function&&, int, int) [with Iterator = __gnu_cxx::__normal_iterator<double*, std::vector<double> >, Function = void (&)(double&)]::<lambda()>::__f’ invalidly declared function type

Как решить эту проблему ?

РЕДАКТИРОВАТЬ: эта новая версия компилируется, но не дает хороший результат:

// parallel_for.cpp
// compilation: g++ -O3 -std=c++0x parallel_for.cpp -o parallel_for -lpthread
// execution: time ./parallel_for 100 50000000
// (100: number of threads, 50000000: vector size)
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <vector>
#include <thread>
#include <cmath>
#include <algorithm>
#include <numeric>
#include <utility>

// Parallel for
template<typename Iterator, class Function>
void parallel_for(const Iterator& first, const Iterator& last, Function&& f, const int nthreads = 1, const int threshold = 1000)
{
const unsigned int group = std::max(std::max(1, std::abs(threshold)), (last-first)/std::abs(nthreads));
std::vector<std::thread> threads;
for (Iterator it = first; it < last; it += group) {
threads.push_back(std::thread([=, &f](){std::for_each(it, std::min(it+group, last), f);}));
}
std::for_each(threads.begin(), threads.end(), [](std::thread& x){x.join();});
}

// Function to apply
template<typename Type>
void f(Type& x)
{
x = std::sin(x)+std::exp(std::cos(x))/std::exp(std::sin(x));
}

// Main
int main(int argc, char* argv[]) {

const unsigned int nthreads = (argc > 1) ? std::atol(argv[1]) : (1);
const unsigned int n = (argc > 2) ? std::atol(argv[2]) : (100000000);
double x = 0;
double y = 0;
std::vector<double> v(n);

std::iota(v.begin(), v.end(), 0);
std::for_each(v.begin(), v.end(), f<double>);
for (unsigned int i = 0; i < n; ++i) x += v[i];

std::iota(v.begin(), v.end(), 0);
parallel_for(v.begin(), v.end(), f<double>, nthreads);
for (unsigned int i = 0; i < n; ++i) y += v[i];

std::cout<<std::setprecision(15)<<x<<" "<<y<<std::endl;
return 0;
}

Результат:

./parallel_for 1 100
155.524339894552 4950

Параллельная версия возвращает 4950, тогда как последовательная версия возвращает 155 …..
В чем проблема ?

5

Решение

  • Вы должны захватить функции по ссылке.

    [=, &f] () { /* your code */ };

  • Посмотри на код.

    #include <iostream>
    
    template <class T>
    void foo(const T& t)
    {
    const int a = t;
    [&]
    {
    std::cout << a << std::endl;
    }();
    }int main()
    {
    foo(42);
    return 0;
    }
    

    Лязг дает вывод 42, но g ++ выдает предупреждение: ‘a’ is used uninitialized in this functionи печатает 0, Похоже, ошибка.

    Временное решение: использовать const auto(для переменной group в вашем коде).

    UPDЯ думаю, вот и все. http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52026

1

Другие решения

Вам нужно преобразование типов или типов (в последнюю очередь). Причина в том, что преобразования типов никогда не выполняются при выводе аргументов шаблона.

Это работает просто отлично (также устраняет проблему, обнаруженную DeadMG и Беном Фойгтом).
Обе версии дают 156608294.151782 с n = 100000000.

template<typename Iterator, class Function>
void parallel_for(const Iterator& first, const Iterator& last, Function&& f, const int nthreads = 1, const int threshold = 1000)
{
const unsigned int group = std::max(std::max(ptrdiff_t(1), ptrdiff_t(std::abs(threshold))), ((last-first))/std::abs(nthreads));
std::vector<std::thread> threads;
threads.reserve(nthreads);
Iterator it = first;
for (; it < last-group; it += group) {
threads.push_back(std::thread([=,&f](){std::for_each(it, std::min(it+group, last), f);}));
}
std::for_each(it, last, f); // last steps while we wait for other threads

std::for_each(threads.begin(), threads.end(), [](std::thread& x){x.join();});
}

С шага for_each(it, last, f) меньше, чем другие, мы можем также использовать вызывающий поток, чтобы завершить это, ожидая других результатов.

5

Одна проблема в том, что it += group может быть last юридически, но создание значения от конца — неопределенное поведение. Просто проверка it < last слишком поздно, чтобы это исправить.

Вам нужно вместо этого проверить last - it в то время как it все еще в силе. нет (ни it + group ни last - group обязательно будет безопасным, хотя последнее должно быть связано с тем, как group рассчитывается.)

Например:

template<typename Iterator, class Function>
void parallel_for(const Iterator& first, const Iterator& last, Function f, const int nthreads = 1, const int threshold = 100)
{
const unsigned int group = std::max(std::max(1, std::abs(threshold)), (last-first)/std::abs(nthreads));
std::vector<std::thread> threads;
threads.reserve(nthreads);
Iterator it = first;
for (; last - it > group; it += group) {
threads.push_back(std::thread([=, &f](){std::for_each(it, it+group, last), f);}));
}
threads.push_back(std::thread([=, &f](){std::for_each(it, last, f);}));

std::for_each(threads.begin(), threads.end(), [](std::thread& x){x.join();});
}
1

Вы даете std::min(it+group, last) в std::for_each, но всегда добавляю group до конца. Это означает, что если last не кратно group на от it, ты будешь двигаться it прошлое last, который является UB.

0

Вам нужно захватить по ссылке, и вам нужно преобразование типов или типов (в последнюю очередь).
Причина в том, что преобразования типов никогда не выполняются при выводе аргументов шаблона.

Кроме того, устраните проблему, обнаруженную DeadMG, и вы получите приведенный ниже код.

Работает просто отлично, обе версии дают 156608294.151782 с n = 100000000.

template<typename Iterator, class Function>
void parallel_for(const Iterator& first, const Iterator& last, Function&& f, const int nthreads = 1, const int threshold = 1000)
{
const unsigned int group = std::max(std::max(ptrdiff_t(1), ptrdiff_t(std::abs(threshold))), ((last-first))/std::abs(nthreads));
std::vector<std::thread> threads;
Iterator it = first;
for (; it < last-group; it += group) {
threads.push_back(std::thread([=,&f](){std::for_each(it, std::min(it+group, last), f);}));
}
std::for_each(it, last, f); // use calling thread while we wait for the others
std::for_each(threads.begin(), threads.end(), [](std::thread& x){x.join();});
}
0

Решение vc11, пожалуйста, дайте мне знать, если оно не работает с gcc.

template<typename Iterator, class Function>
void parallel_for( const Iterator& first, const Iterator& last, Function&& f, const size_t nthreads = std::thread::hardware_concurrency(), const size_t threshold = 1 )
{
const size_t portion = std::max( threshold, (last-first) / nthreads );
std::vector<std::thread> threads;
for ( Iterator it = first; it < last; it += portion )
{
Iterator begin = it;
Iterator end = it + portion;
if ( end > last )
end = last;

threads.push_back( std::thread( [=,&f]() {
for ( Iterator i = begin; i != end; ++i )
f(i);
}));
}
std::for_each(threads.begin(), threads.end(), [](std::thread& x){x.join();});
}
0
По вопросам рекламы [email protected]