Ik vraag me af of er een lichte, ongecompliceerde manier is om lussen zoals for
en op bereik gebaseerde-for
lussen parallel te laten berekenen in C++. Hoe zou je zoiets implementeren? Van Scala ken ik de functies map
, filter
en foreach
en misschien is het ook mogelijk om deze parallel uit te voeren? Is er een gemakkelijke manier om dit te bereiken in C++?
Mijn primaire platform is Linux, maar het zou fijn zijn als het platformonafhankelijk zou werken.
Antwoord 1, autoriteit 100%
Met de parallelle algoritmen in C++17 kunnen we nu het volgende gebruiken:
std::vector<std::string> foo;
std::for_each(
std::execution::par_unseq,
foo.begin(),
foo.end(),
[](auto&& item)
{
//do stuff with item
});
om lussen parallel te berekenen. De eerste parameter specificeert het uitvoeringsbeleid
Antwoord 2, autoriteit 35%
Wat is uw platform? Je kunt naar OpenMPkijken, hoewel het geen onderdeel is van C++. Maar het wordt breed ondersteund door compilers.
Voor op bereik gebaseerde for-loops, zie bijvoorbeeld OpenMP met C++11-reeksgebaseerde for-loops?.
Ik heb ook weinig documenten gezien op http://www.open-std.orgdie wijzen op enige inspanningen om parallelle constructies/algoritmen op te nemen in toekomstige C++, maar weten niet wat hun huidige status is.
UPDATE
Gewoon een voorbeeldcode toevoegen:
template <typename RAIter>
void loop_in_parallel(RAIter first, RAIter last) {
const size_t n = std::distance(first, last);
#pragma omp parallel for
for (size_t i = 0; i < n; i++) {
auto& elem = *(first + i);
// do whatever you want with elem
}
}
Het aantal threads kan tijdens runtime worden ingesteld via de omgevingsvariabele OMP_NUM_THREADS
.
Antwoord 3, autoriteit 26%
std::async
kan een past hier goed, als u de C++
runtime het parallellisme wilt laten regelen.
Voorbeeld van cppreference.com:
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <future>
template <typename RAIter>
int parallel_sum(RAIter beg, RAIter end)
{
auto len = end - beg;
if(len < 1000)
return std::accumulate(beg, end, 0);
RAIter mid = beg + len/2;
auto handle = std::async(std::launch::async,
parallel_sum<RAIter>, mid, end);
int sum = parallel_sum(beg, mid);
return sum + handle.get();
}
int main()
{
std::vector<int> v(10000, 1);
std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n';
}
Antwoord 4, autoriteit 22%
Met C++11 kun je een for-lus parallelliseren met slechts een paar regels codes.
Mijn functie parallel_for()
(definieer later in de post) splitst een for-lus in kleinere chunks (sub-lussen), en elk chunk wordt toegewezen aan een thread. Hier is het gebruik:
/// Say you want to parallelize this:
for(int i = 0; i < nb_elements; ++i)
computation(i);
/// Then you would do:
parallel_for(nb_elements, [&](int start, int end){
for(int i = start; i < end; ++i)
computation(i);
});
Mijn parallel_for()
werkt ook binnen een klas:
struct My_obj {
/// Replacing:
void sequential_for(){
for(int i = 0; i < nb_elements; ++i)
computation(i);
}
/// By:
void process_chunk(int start, int end)
{
for(int i = start; i < end; ++i)
computation(i);
}
void threaded_for(){
parallel_for(nb_elements, [this](int s, int e){
this->process_chunk(s, e);
} );
}
};
Eindelijk is hier de implementatie van parallel_for()
, plak het in een headerbestand en gebruik het naar believen:
#include <algorithm>
#include <thread>
#include <functional>
#include <vector>
/// @param[in] nb_elements : size of your for loop
/// @param[in] functor(start, end) :
/// your function processing a sub chunk of the for loop.
/// "start" is the first index to process (included) until the index "end"
/// (excluded)
/// @code
/// for(int i = start; i < end; ++i)
/// computation(i);
/// @endcode
/// @param use_threads : enable / disable threads.
///
///
static
void parallel_for(unsigned nb_elements,
std::function<void (int start, int end)> functor,
bool use_threads = true)
{
// -------
unsigned nb_threads_hint = std::thread::hardware_concurrency();
unsigned nb_threads = nb_threads_hint == 0 ? 8 : (nb_threads_hint);
unsigned batch_size = nb_elements / nb_threads;
unsigned batch_remainder = nb_elements % nb_threads;
std::vector< std::thread > my_threads(nb_threads);
if( use_threads )
{
// Multithread execution
for(unsigned i = 0; i < nb_threads; ++i)
{
int start = i * batch_size;
my_threads[i] = std::thread(functor, start, start+batch_size);
}
}
else
{
// Single thread execution (for easy debugging)
for(unsigned i = 0; i < nb_threads; ++i){
int start = i * batch_size;
functor( start, start+batch_size );
}
}
// Deform the elements left
int start = nb_threads * batch_size;
functor( start, start+batch_remainder);
// Wait for the other thread to finish their task
if( use_threads )
std::for_each(my_threads.begin(), my_threads.end(), std::mem_fn(&std::thread::join));
}
Ten slotte kunt u macro’s definiëren om een nog compactere uitdrukking te krijgen:
#define PARALLEL_FOR_BEGIN(nb_elements) parallel_for(nb_elements, [&](int start, int end){ for(int i = start; i < end; ++i)
#define PARALLEL_FOR_END()})
Bezig met het converteren van een sequentie voor:
for(int i = 0; i < nb_elements; ++i)
computation(i);
Is alleen een kwestie van doen:
PARALLEL_FOR_BEGIN(nb_edges)
{
computation(i);
}PARALLEL_FOR_END();
Antwoord 5, autoriteit 5%
Dit kan worden gedaan met behulp van threads
specifiek pthreads
bibliotheekfunctie die kan worden gebruikt om gelijktijdig bewerkingen uit te voeren.
Je kunt hier meer over hen lezen: http://www.tutorialspoint.com/cplusplus/ cpp_multithreading.htm
std::thread kan ook worden gebruikt: http://www.cplusplus.com/ referentie/thread/thread/
Hieronder staat een code waarin ik de thread-ID van elke thread gebruik om de array in twee helften te splitsen:
#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 2
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
void *splitLoop(void *threadid)
{
long tid;
tid = (long)threadid;
//cout << "Hello World! Thread ID, " << tid << endl;
int start = (tid * 5);
int end = start + 5;
for(int i = start;i < end;i++){
cout << arr[i] << " ";
}
cout << endl;
pthread_exit(NULL);
}
int main ()
{
pthread_t threads[NUM_THREADS];
int rc;
int i;
for( i=0; i < NUM_THREADS; i++ ){
cout << "main() : creating thread, " << i << endl;
rc = pthread_create(&threads[i], NULL,
splitLoop, (void *)i);
if (rc){
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
Onthoud ook tijdens het compileren u moet de -lpthread
vlag gebruiken.
Link naar oplossing op ideone: http://ideone.com/kcsw4p
Antwoord 6, autoriteit 3%
De Concurrency::parallel_for (PPL) is ook een van de leuke opties om taakparallellisme te doen.
Genomen uit C++-coderingsoefening – Parallel For – Monte Carlo PI-berekening
int main() {
srand(time(NULL)); // seed
const int N1 = 1000;
const int N2 = 100000;
int n = 0;
int c = 0;
Concurrency::critical_section cs;
// it is better that N2 >> N1 for better performance
Concurrency::parallel_for(0, N1, [&](int i) {
int t = monte_carlo_count_pi(N2);
cs.lock(); // race condition
n += N2; // total sampling points
c += t; // points fall in the circle
cs.unlock();
});
cout < < "pi ~= " << setprecision(9) << (double)c / n * 4.0 << endl;
return 0;
}