繁体   English   中英

高效创建线程池(C++)

[英]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::asynchstd::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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM