c++多线程编程:实现标准库accumulate函数的并行计算版本
2016-02-03 21:31
846 查看
今天使用c++实现了标准库头文件<numeric>中的accumulate函数的并行计算版本,代码如下,注释写的比较详细,仅对其中几点进行描述:
①该实现假定不发生任何异常,故没有对可能产生的异常进行处理
②第42行的语句:
const unsigned int num_thread = std::min((hardware_thread != 0 ? hardware_thread : 2), max_thread);
要运行的线程数是计算出的最大线程数和硬件线程数量的较小值。这是因为若运行的线程数超出了硬件支持的范围,CPU的上下文切换会降低性能。又因为hardware_thread的值可能为0,在这种情况下用户需要自行替换线程的数量,在代码中为2,因为在单核的机器上运行过多的线程会导致性能降低,但过少的线程也会使用户错过可用的并发。
③第44行计算每个线程操作的元素个数时算式可能无法整除,但无须担心,因为最后一个线程将会处理剩下的所有元素,如第68行语句所示:
Accum<Iterator, T>()(block_begin, last, results[num_thread - 1]);
④注意第64行的语句:
threads[i] = thread(Accum<Iterator, T>(), block_begin, block_end, std::ref(results[i]));
传给线程执行的函数的第三个参数增加了std::ref(),该函数包含在头文件<functional>中。在一般情况下,thread对象的构造函数只是简单地拷贝用户提供的参数,然后传递给线程关联的可调用对象。也就是说,该可调用对象接收的是该参数的副本,对其所作的修改无法影响到最初用户所传递的参数。若需要接收参数的引用版本,则需要使用std::ref()函数。
①该实现假定不发生任何异常,故没有对可能产生的异常进行处理
②第42行的语句:
const unsigned int num_thread = std::min((hardware_thread != 0 ? hardware_thread : 2), max_thread);
要运行的线程数是计算出的最大线程数和硬件线程数量的较小值。这是因为若运行的线程数超出了硬件支持的范围,CPU的上下文切换会降低性能。又因为hardware_thread的值可能为0,在这种情况下用户需要自行替换线程的数量,在代码中为2,因为在单核的机器上运行过多的线程会导致性能降低,但过少的线程也会使用户错过可用的并发。
③第44行计算每个线程操作的元素个数时算式可能无法整除,但无须担心,因为最后一个线程将会处理剩下的所有元素,如第68行语句所示:
Accum<Iterator, T>()(block_begin, last, results[num_thread - 1]);
④注意第64行的语句:
threads[i] = thread(Accum<Iterator, T>(), block_begin, block_end, std::ref(results[i]));
传给线程执行的函数的第三个参数增加了std::ref(),该函数包含在头文件<functional>中。在一般情况下,thread对象的构造函数只是简单地拷贝用户提供的参数,然后传递给线程关联的可调用对象。也就是说,该可调用对象接收的是该参数的副本,对其所作的修改无法影响到最初用户所传递的参数。若需要接收参数的引用版本,则需要使用std::ref()函数。
//实现标准库头文件<numeric>中accumulate函数的并行版本 #include <iostream> #include <thread> #include <numeric> #include <algorithm> #include <vector> #include <functional> #include <utility> using std::thread; using std::vector; using std::accumulate; using std::cout; using std::endl; template <typename Iterator, typename T> class Accum { public: void operator() (Iterator first, Iterator last, T &sum) { sum = std::accumulate(first, last, sum); } }; template <typename Iterator, typename T> T ParallelAccum(Iterator first, Iterator last, T &sum) { //计算迭代器中包含的元素数量 const unsigned int len = std::distance(first, last); //若迭代器中没有元素则直接返回 if (!len) { return sum; } //每个线程处理的元素的最小数量 const unsigned int min_per_thread = 25; //获取线程的最大数量,向上取整 const unsigned int max_thread = (len - 1 + min_per_thread) / min_per_thread; //获取机器支持的并发线程数 const unsigned int hardware_thread = thread::hardware_concurrency(); //取上述两者的较小值,同时避免线程数过少 const unsigned int num_thread = std::min((hardware_thread != 0 ? hardware_thread : 2), max_thread); //最终实际上每个线程处理的元素个数 const unsigned int block_size = len / num_thread; //保存每个线程累加的结果 vector<T> results(num_thread); //启动比num_thread - 1个线程,因为main函数本身已开启一个线程 vector<thread> threads(num_thread - 1); // cout << "Number of elements: " << len << endl; cout << "Hardware concurrency: " << hardware_thread << endl; cout << "Maximum number of threads: " << max_thread << endl; cout << "Number of threads: " << num_thread << endl; cout << "Block size: " << block_size << endl; cout << "Started parallel calculating..." << endl; //开始并行计算 Iterator block_begin = first; for (unsigned int i = 0; i < (num_thread - 1); ++i) { Iterator block_end = block_begin; //将迭代器向前推进一个块,到达当前块的末尾位置 std::advance(block_end, block_size); //传递参数,通常情况下thread的构造函数将复制所提供的参数,需要将模板参数转为引用 threads[i] = thread(Accum<Iterator, T>(), block_begin, block_end, std::ref(results[i])); block_begin = block_end; } //处理最后一个线程,由于block_size = len / num_thread得到的结果不一定为整数,该线程处理剩余的所有元素 Accum<Iterator, T>()(block_begin, last, results[num_thread - 1]); //对threads中所有线程调用join() std::for_each(threads.begin(), threads.end(), std::mem_fn(&thread::join)); // return accumulate(results.begin(), results.end(), sum); } int main() { vector<int> i_vec; int sum = 0; for (int i = 1; i != 501; ++i) { i_vec.push_back(i); } sum = ParallelAccum(i_vec.cbegin(), i_vec.cend(), sum); cout << "sum = " << sum << endl; system("pause"); return 0; }
相关文章推荐
- 小蚂蚁学习C语言(39)——题目——求一个数字是否是玫瑰花数
- C++编程
- 详解c++指针的指针和指针的引用
- (转)C++ stringstream介绍,使用方法与例子
- C++中数字与字符串之间的转换(使用CString.Format或者sprintf)
- C++关于类的强制类型转换
- c++中string常用函数整理<引用>
- C++内存管理(超长)
- C++test 关于resource参数和include/exclude参数说明
- 学生信息管理系统-c语言(文本菜单系统)
- c++一些关于输出格式的函数
- 聊天机器人--C语言(非智能+初级简单)
- C++静态库中使用_declspec(dllexport) 不能导出函数的问题
- [LeetCode] 130
- C++链队列类实现
- [Leetcode] 331
- C语言实现区块加密
- 菜鸟系列之C/C++经典试题(七)
- 【C/C++】宏
- C++学习笔记1