C++线程开销

5

我正在尝试在C++中使用线程,特别是将它们用于并行化映射操作。

以下是代码:

#include <thread>
#include <iostream>
#include <cstdlib>
#include <vector>
#include <math.h>
#include <stdio.h>

double multByTwo(double x){
  return x*2;
}

double doJunk(double x){
  return cos(pow(sin(x*2),3));
}

template <typename T>
void map(T* data, int n, T (*ptr)(T)){
  for (int i=0; i<n; i++)
    data[i] = (*ptr)(data[i]);
}

template <typename T>
void parallelMap(T* data, int n, T (*ptr)(T)){
  int NUMCORES = 3;
  std::vector<std::thread> threads;
  for (int i=0; i<NUMCORES; i++)
    threads.push_back(std::thread(&map<T>, data + i*n/NUMCORES, n/NUMCORES, ptr));
  for (std::thread& t : threads)
    t.join();
}

int main()
{
  int n = 1000000000;
  double* nums = new double[n];
  for (int i=0; i<n; i++)
    nums[i] = i;

  std::cout<<"go"<<std::endl;

  clock_t c1 = clock();

  struct timespec start, finish;
  double elapsed;

  clock_gettime(CLOCK_MONOTONIC, &start);

  // also try with &doJunk
  //parallelMap(nums, n, &multByTwo);
  map(nums, n, &doJunk);

  std::cout << nums[342] << std::endl;

  clock_gettime(CLOCK_MONOTONIC, &finish);

  printf("CPU elapsed time is %f seconds\n", double(clock()-c1)/CLOCKS_PER_SEC);

  elapsed = (finish.tv_sec - start.tv_sec);
  elapsed += (finish.tv_nsec - start.tv_nsec) / 1000000000.0;

  printf("Actual elapsed time is %f seconds\n", elapsed);
}

使用multByTwo函数时,并行版本实际上略慢(1.01秒对比0.95秒的实际时间),而使用doJunk函数时快了(51对比136个实际时间)。这对我意味着:

  1. 并行化正在起作用
  2. 声明新线程的开销非常大。你有什么想法为什么开销如此之大,如何避免?

2
请注意,这并不一定是特定于C++本地线程的问题,而是与您使用的实现和编译器有关。因此,很难给出明确的答案。 - zxcdw
你在哪种硬件上运行这段代码?处理器类型和插槽数量?内存大小?操作系统?编译器版本? - Hristo Iliev
4个回答

7

猜测一下:你看到的可能是multByTwo代码非常快,以至于你已经达到了内存饱和。无论你投入多少处理器性能,该代码都不会运行得更快,因为它已经尽可能快地获取和写入RAM中的位。


这看起来是正确的。OP 有一个8GB的数据集。在1.01秒内完成8GB的处理对于高端的 Nehalem 或低端的 Sandy Bridge 处理器来说是合理的速度。 - Mysticial

3
您没有说明测试程序的硬件,编译器版本和操作系统。我在我们的四插槽Intel Xeon系统上使用64位Scientific Linux和源代码编译的

2

在多核机器上,多个线程才能在更短的时间内完成更多的工作。

否则它们只是以轮询方式交替执行。


我不是在谈论“感知”的性能和用户界面。我在谈论真正的工作。如果只有一个处理器,那么一次只能运行一个线程。 - Steve Wellens
这是正确的,但操作系统如何分配线程的时间会有很大的差异。我曾经在现实世界中看到过这种情况,在学校被迫编写的应用程序(远在多核之前)通过线程大幅提高了性能。看看我的回答,我解释了轮转效应,虽然我没有使用那个术语,但解释了为什么多线程应用程序会获得更多的处理器时间片! - trumpetlicks
有些操作系统会从其他应用程序中夺取处理器时间并将其分配给多线程应用程序?我不知道这一点。看起来滥用的可能性很高。 - Steve Wellens
通常情况下,这完全取决于在优先级队列中分配的优先级。再次举个例子,我的例子解释了一个情况,即所有线程和进程都在同一优先级水平下保持。因此,当添加新线程时,它会被赋予与所有其他进程/线程相同的时间权重(即1 / totalThreadCount)。总线程越多,每个线程的时间就越短,但对于具有最多线程的应用程序来说,时间就越长!是的,它可以轻松地被利用!想想一个多线程3D渲染应用程序,当3D正在处理时,其他应用程序获得的周期非常少。 - trumpetlicks

0
生成新线程可能是一项昂贵的操作,这取决于平台。避免此开销的最简单方法是在程序启动时生成少量线程并拥有某种作业队列。我相信std::async会为您完成此操作。

1
OP 只会生成它们一次 - 而且任务相当大 n = 1000000000。因此,我不认为这是问题所在。 - Mysticial
我相信,如果线程数少于std::thread::hardware_concurrency()返回的数量,最终结果将是相同的。 - manasij7479

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接