[英]Efficient creation of thread pool (C++)
创建线程池以提高计算效率的“最佳”方法是什么?
假设我有以下代码来打印给定间隔内有多少素数(仅用于演示,我知道它非常慢):
#include <future>
#include <iostream>
#include <thread>
#include <math.h>
bool is_prime(int n) {
if (n == 2 || n == 3) {
return 1;
}
else if (n % 2 == 0 || n % 3 == 0) {
return 0;
}
for (int i = 5; i < sqrt(n) + 1; i = i + 6) {
if (n % i == 0 || n % (i+2) == 0) {
return 0;
}
}
return 1;
}
int primes_in_range(int a, int b) {
int total = 0;
for (int i = a; i <= b; i++) {
total += is_prime(i);
}
return total;
}
int main() {
int total = primes_in_range(2, 10000000);
std::cout << total << std::endl;
}
如果我想通过将间隔分成更小的线程块来加快运行速度,我该怎么做?
目前,我正在做这样的事情:
auto thread1 = std::async(std::launch::async, primes_in_range, 2, 2500000);
auto thread2 = std::async(std::launch::async, primes_in_range, 2500001, 5000000);
auto thread3 = std::async(std::launch::async, primes_in_range, 5000001, 7500000);
auto thread4 = std::async(std::launch::async, primes_in_range, 7500001, 10000000);
int total1 = thread1.get();
int total2 = thread2.get();
int total3 = thread3.get();
int total4 = thread4.get();
std::cout << total1 + total2 + total3 + total4 << std::endl;
但这似乎不是很有效,特别是如果我尝试说n
线程。
有什么更好的方法呢? 一般来说,我对多线程还很陌生,所以如果我做错了什么,请告诉我!
考虑您将按顺序计算间隔的结果。 然后你会使用循环,你可以对std::asynch
和std::future
做同样的事情( std::asynch
不返回线程)。
auto get_future_chunk(int from, int to){
return std::async(std::launch::async, primes_in_range, from,to);
}
int main() {
std::vector<decltype(get_future_chunk(0,0))> futures;
int from = 2;
int chunk_size = 5000000;
const int max = 10000000;
int to = from+chunk_size;
while (to <= max) {
futures.push_back(get_future_chunk(from,to));
from = to + 1;
to += chunk_size;
}
futures.push_back(get_future_chunk(from,max));
int total = 0;
for (auto& f : futures) total += f.get();
std::cout << total << "\n";
}
我写这个函数的唯一原因是我懒得去查找从asynch
返回的future的确切类型,并且在函数的帮助下我可以更容易地推断出它。 为了在 Godbolt 上进行测试,我只使用了两个块,因为在请求大量线程时,我收到了资源不可用的错误。 该代码修复了chunk_size
并基于此确定要生成的期货数量。 反过来当然也是可能的:固定块的数量,然后计算区间界限。
另一种解决方案是简单地使用目前所有主流 C++ 编译器都支持的 OpenMP(例如 GCC、Clang、ICC 甚至 MSVC,尽管有一些快速入门)。 您只需要使用-fopenmp
编译代码并使用#pragma omp parallel reduction(+:total)
指令,OpenMP 将创建一个线程池,并行计算操作并执行同步。 它还回收线程池,以便只创建一次(除非您更改线程数或类似的东西)。 这是一个例子:
int primes_in_range(int a, int b) {
int total = 0;
#pragma omp parallel reduction(+:total)
for (int i = a; i <= b; i++) {
total += is_prime(i);
}
return total;
}
OpenMP 使您可以调整调度,这在此处很有用,因为is_prime
需要可变的时间,因此负载平衡可以带来更好的性能( schedule(guided)
子句应该会带来更快的性能,但最好简单地测试可用的调度)。 如果您需要延迟计算,您可以使用 OpenMP 任务原语,这些原语使用起来也非常简单(并且通过让 OpenMP 运行时完成所有硬/无聊/容易出错的工作,它避免了再次重新实现轮子)你)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.