所以我正在查找如何使用stl c ++的东西做一些并行操作,并在Stack Overflow中的另一个问题上找到以下代码
template <typename RAIter> //FOUND ON STACK OVERFLOW
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();
}
我想制作一个通用的parallel_for_each函数,它循环一个(希望是)任意容器类型并将算法应用于每个条目,所以我将上面的内容修改为:
template <typename ContainerIterator, typename containerSizeType, typename AlgorithmPerEntry> //modified version of parallel sum code above : https://stackoverflow.com/questions/36246300/parallel-loops-in-c
void parallel_for_each(ContainerIterator beg, ContainerIterator end, AlgorithmPerEntry& algorithm, containerSizeType maxProbSize)
{
containerSizeType len = end - beg;
if (len < maxProbSize){//if you are sufficiently small, go ahead and execute
std::for_each(beg, end, algorithm);
std::cout << "working on processor with id = " << GetCurrentProcessorNumber() << std::endl;//the processor id's change so I'm assuming this is executing in parallel
return;
}
//otherwise, continue spawning more threads
ContainerIterator mid = beg + len / 2;
auto handle = std::async(std::launch::async,
parallel_for_each<ContainerIterator, containerSizeType, AlgorithmPerEntry>, mid, end, algorithm, maxProbSize);
parallel_for_each(beg, mid, algorithm, maxProbSize);
handle.get(); //corrected as advised
}
我想测试的是一个超级简单的仿函数,所以我做了以下内容:
template<typename T>
struct dataSetter
{
const T& set_to;
dataSetter(const T& set_to_in) : set_to(set_to_in){}
void operator()(T& set_this)
{
set_this = set_to;
}
};
非常简单,只需将某些arg的值设置为其operator()
即可这是我的主要功能的主体
std::vector<int> ints(100000);
unsigned minProbSize = 1000;
int setval = 7;
dataSetter<int> setter(setval);
parallel_for_each(ints.begin(), ints.end(), setter, minProbSize);//parallel assign everything to 7
//some sort of wait function to go here?
std::cout << std::endl << "PS sum of all ints = " << parallel_sum(ints.begin(), ints.end()) << std::endl; //parallel sum the entries
int total = 0;//serial sum the entries
for (unsigned i = 0; i < ints.size(); i++)
total += ints[i];
std::cout << std::endl << "S sum of all ints = " << total << std::endl;
std::cout << std::endl << "PS sum of all ints = " << parallel_sum(ints.begin(), ints.end()) << std::endl; //parallel sum the entries again
以下是一些输出:
PS sum of all ints = 689052
S sum of all ints = 700000
PS sum of all ints = 700000
另一次运行的输出:
PS sum of all ints = 514024
S sum of all ints = 700000
PS sum of all ints = 700000
它始终获得向量低的第一个并行和。我猜测发生了什么是所有的赋值线程都被创建,然后求和线程被创建,但是某些求和线程正在过早地执行(在最后一个赋值线程之前)。有什么方法可以迫使我等一下吗?和往常一样,我对所有建议持开放态度。
答案 0 :(得分:1)
MSVS 2013附带非标准兼容的std异步。据我所知,这是故意的。
当使用异步启动策略调用时,此非兼容的std异步无法返回阻止任务完成的期货。
结果是你的代码是正确的,但你的编译器坏了。要么升级到2015/2017,要么在它超出范围之前添加handle.get()
。
就个人而言,我会以不同的方式编写这样的实用程序。
auto launch_async = [](auto&& f){
return std::async( std::launch::async, decltype(f)(f) );
};
template <class Linear, class Executor=decltype(launch_async) const&>
auto parallel_algo(Linear&& linear, std::size_t chunk_size, Executor&& exec=launch_async){
return
[
linear=std::forward<Linear>(linear),
chunk_size,
exec=std::forward<Executor>(exec)
]
( auto start, auto finish )
{
std::size_t count = finish-start;
if (count <= chunk_size) {
linear( start, finish);
return;
}
std::size_t par = (count+chunk_size-1)/chunk_size;
std::vector<std::future<void>> tasks( par-1 );
auto task=[&]( auto i ){
auto b = start+( count*i/par );
auto e = start+( count*(i+1)/par );
return [b,e,linear]{ linear(b,e); };
};
for(auto& f:tasks){
auto i = &f-tasks.data();
f = exec( task(i) );
}
task(par-1)();
for (auto&f:tasks) f.get();
};
}
template<class F>
auto foreacher( F&&f ){
return [f=std::forward<F>(f)]( auto b, auto e ){
for (auto i=b; i!=e; ++i) f(*i);
};
}
是C ++ 14,但可以在C ++ 11中进行模拟。
这样做是采用线性算法和最大块大小,返回并行算法。
在这两种情况下,算法都采用迭代器范围。
当我在那里时,并行算法工厂需要一个执行程序。您可以编写一个exectuor,例如一个线程池,以避免不必要地生成太多线程。单元素算法使用foreacher将自己提升到范围算法。
一些玩具执行者:
auto launch_deferred = [](auto&& f){
return std:async( std::launch::deferred, decltype(f)(f) );
};
template<class F, class R=std::result_of_t< decltype(f)() >>
std::enable_if_t< !std::is_same<R,void>, std::future<R> >
make_ready_future( F&& f ) {
std::promise<R> p;
try {
p.set_value( decltype(f)(f)() );
} catch( ... ) {
p.set_exception(std::current_exception());
}
return p.get_future();
}
template<class F, class R=std::result_of_t< decltype(f)() >>
std::enable_if_t< std::is_same<R,void>, std::future<R> >
make_ready_future( F&& f ) {
std::promise<void> p;
try {
decltype(f)(f)();
p.set_value();
} catch( ... ) {
p.set_exception(std::current_exception());
}
return p.get_future();
}
auto launch_ready = [](auto&& f){
return make_ready_future( decltype(f)(f) );
};
这两个都使你的并行代码在一个线程中运行。
一个更高级的人在线程池中排队任务并同样返回期货。
这是测试代码:
std::vector<int> v(10000);
parallel_algo( foreacher([](auto&x){x=7;}), 100 )( v.begin(), v.end() );
std::atomic<int> total(0);
parallel_algo( foreacher([&total](auto&x){total+=x;}), 100 )( v.begin(), v.end() );
std::cout << total << "\n";