关闭

C++并发实战5:并行化的std::accumulate

标签: C++并发实战5并行化的stdaccum
2679人阅读 评论(1) 收藏 举报
分类:

            std::thread::hardware_concurrency()可以返回CPU个数,在C++库不能获取CPU信息下可能返回0。该信息是非常有用的,当线程数超过CPU cores会频繁的引起上下文切换,返回会降低性能,当hardware_concurrency()返回0时可以指定一个数目比如2。

          下面给出一个并行化的std::accumulate()的例子:std::accumulate(first,last,init,fun)的语义是在一个初始值init前提下将一个[first,last)序列的元素与init执行fun函数。默认fun=NULL是将[first,last)的所有元素加起来再加上init得到序列的累加值。现在并行化:将序列拆分成子序列然后每个子序列的求和由一个线程执行,然后将各个线程的累加值再求和。有点类似于MapReduce的wordcount。

#include<iostream>
#include<thread>
#include<numeric>
#include<algorithm>
#include<functional>
#include<vector>
using namespace std;
template<typename Iterator,typename T>
struct accumulate_block{
    void operator()(Iterator first,Iterator last,T& result){//每个子序列累加,不能通过线程的返回值返回累加结果,而是通过一个result引用将结果返回给主线程
        result=std::accumulate(first,last,result);
    }
};

template<typename Iterator,typename T>
T parallel_accumulate(Iterator first,Iterator last,T init)
{
    unsigned long const length=std::distance(first,last);//计算序列的长度
    if(!length)//若序列为空直接返回init
        return init;
    unsigned long const min_per_thread=25;//每个线程的子序列大小
    unsigned long const max_threads=(length+min_per_thread-1)/min_per_thread;//计算线程数(向上取整)
    unsigned long const hardware_threads=std::thread::hardware_concurrency();//获取PC的CPU core数目,C++库可能无法访问该信息,所以可能返回0
    unsigned long const num_threads=std::min(hardware_threads!=0?hardware_threads:2,max_threads);//最大线程数在计算出来的最大线程数和硬件core数目中取较小者(线程数超过core数目反而降低性能)
    unsigned long const block_size=length/num_threads;//重新计算每个线程需要执行的序列大小
    vector<T> results(num_threads);//每个线程的结果将保存在results中
    vector<thread> threads(num_threads-1);//开启线程池,只用开启num_threads-1个子线程,因为主线程也可以计算一个序列
    Iterator block_start=first;//序列开始位置
    for(int i=0;i<(num_threads-1);i++){//这里只分配子线程的任务序列
        Iterator block_end=block_start;
        std::advance(block_end,block_size);//迭代器block_end向前移动block_size
        threads[i]=thread(accumulate_block<Iterator,T>(),block_start,block_end,std::ref(results[i]));//每个子线程的子序列分配
        block_start=block_end;//更新序列位置
    }
    accumulate_block<Iterator,T>()(block_start,last,results[num_threads-1]);//主线程的任务,注意是last
    std::for_each(threads.begin(),threads.end(),std::mem_fn(&thread::join));//等待子线程完成
    return std::accumulate(results.begin(),results.end(),init);//汇总结果
}
int main(){
    vector<int> vec;
    for(int i=0;i<100;i++){
        vec.push_back(10);
    }
    int sum=parallel_accumulate(vec.begin(),vec.end(),5);
    cout<<"sum="<<sum<<endl;
    return 0;
}

程序输出:

sum=1005



0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:443781次
    • 积分:6130
    • 等级:
    • 排名:第4221名
    • 原创:187篇
    • 转载:7篇
    • 译文:1篇
    • 评论:82条
    博客专栏
    最新评论