C++ 不完整的多线程光线跟踪器占用的时间是预期的两倍

C++ 不完整的多线程光线跟踪器占用的时间是预期的两倍,c++,multithreading,graphics,parallel-processing,raytracing,C++,Multithreading,Graphics,Parallel Processing,Raytracing,我正在制作一个MT-Ray跟踪器多线程,正如标题所说,它的执行时间是单线程版本的两倍。显然,其目的是将渲染时间减少一半,但是我现在所做的只是发送光线跟踪方法运行两次,每个线程一次,基本上执行相同的渲染两次。尽管如此,由于线程可以并行运行,执行时间不会有明显的增加。但这是关于加倍的 这必须与我的多线程设置有关。我认为这与我将它们创建为可接合的事实有关。因此,我将解释我正在做什么,并把相关的代码,看看是否有人可以确认,如果这是一个问题 我创建了两个线程,并将它们设置为可连接的。创建一个光线跟踪器,分

我正在制作一个MT-Ray跟踪器多线程,正如标题所说,它的执行时间是单线程版本的两倍。显然,其目的是将渲染时间减少一半,但是我现在所做的只是发送光线跟踪方法运行两次,每个线程一次,基本上执行相同的渲染两次。尽管如此,由于线程可以并行运行,执行时间不会有明显的增加。但这是关于加倍的

这必须与我的多线程设置有关。我认为这与我将它们创建为可接合的事实有关。因此,我将解释我正在做什么,并把相关的代码,看看是否有人可以确认,如果这是一个问题

我创建了两个线程,并将它们设置为可连接的。创建一个光线跟踪器,分配足够的内存来存储图像像素(这在构造函数中完成)。运行两次迭代循环以发送每个线程的相关信息,如线程id和Raytracer实例的地址

然后pthread_create调用run_thread,其目的是在完成工作的地方调用ray_tracer:draw方法。在绘图方法上,我有一个

 pthread_exit (NULL); 
作为它的最后一件东西(唯一的MT东西)。然后执行另一个循环来连接线程。最后,我开始在一个小循环中编写文件。最后关闭文件并删除与用于在draw方法中存储图像的数组相关的指针

现在我可能不需要使用加入,因为我没有执行“真正的”多线程光线跟踪器,只是渲染两次,但只要我开始在图像像素之间交替(即,thread0->渲染像素0-thread0->存储像素0,thread1->渲染像素1-thread1->存储像素1,thread0->渲染像素2-thread0->存储像素2,thread1->渲染像素3-thread1->存储像素3,等等…)我想我需要它,以便能够在文件中以正确的顺序写入像素

这是正确的吗?我真的需要在我的方法(或任何其他方法)中使用join吗?如果我这样做,我如何发送线程以并发运行,而不是等待其他线程完成?问题是否与join完全无关

pthread_t threads [2];
thread_data td_array [2];
pthread_attr_t attr;
void *status;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
TGAManager tgaManager ("z.tga",true);
if (tgaManager.isFileOpen()) {
   tgaManager.writeHeadersData (image);
   RayTracer rt (image.getHeight() * image.getWidth());
   int rc;
   for (int i=0; i<2; i++) {
      //cout << "main() : creating thread, " << i << endl;
      td_array[i].thread_id=i;
      td_array[i].rt_ptr = &rt;
      td_array[i].img_ptr = &image;
      td_array[i].scene_ptr = &scene;
      //cout << "td_array.thread_index: " << td_array[i].thread_id << endl;
      rc = pthread_create (&threads[i], NULL, RayTracer::run_thread, &td_array[i]);
  }
   if (rc) {
      cout << "Error:unable to create thread," << rc << endl;
      exit(-1);
   }
   pthread_attr_destroy(&attr);
   for (int i=0; i<2; i++ ) {
      rc = pthread_join(threads[i], &status);
      if (rc) {
         cout << "Error:unable to join," << rc << endl;
         exit(-1);
      }
   }
//tgaManager.writeImage (rt,image.getSize());

  for (int i=0; i<image.getWidth() * image.getHeight(); i++) {
    cout << i << endl;
    tgaManager.file_writer.put (rt.b[i]);
    tgaManager.file_writer.put (rt.c[i]);
    tgaManager.file_writer.put (rt.d[i]);
  }
tgaManager.closeFile(1);
rt.deleteImgPtr (); 
}
pthread_t threads[2];
线程数据td_数组[2];
pthread_attr_t attr;
无效*状态;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,pthread_CREATE_JOINABLE);
tga经理(“z.tga”,正确);
if(tgaManager.isFileOpen()){
tgamager.writeHeadersData(图像);
光线跟踪器rt(image.getHeight()*image.getWidth());
int rc;
对于(int i=0;i您确实希望加入()线程,因为如果不这样做,您会遇到几个问题:

  • 你怎么知道线程什么时候完成了执行?你不想开始写出结果图像,结果却发现在你写出它的那一刻它没有被完全计算出来

  • 如何知道何时可以安全地拆除线程可能正在访问的任何数据结构?例如,
    RayTracer
    对象位于堆栈上,以及(AFAICT)您的线程正在写入其像素数组。如果主函数在线程退出之前返回,则线程很有可能最终写入一个不再存在的
    RayTracer
    对象,这将通过覆盖任何其他可能存在的对象(偶然)而损坏堆栈在函数返回后的相同位置

  • 因此,您肯定需要连接()您的线程;不过,您不需要显式地将它们声明为PTHREAD_CREATE_JOINABLE,因为默认情况下已经设置了该属性

    加入线程不应导致线程速度减慢,只要在对其中任何线程调用join()之前创建并运行了两个线程(在发布的代码中似乎就是这样)

    至于为什么你会看到两个线程的减速,这很难说,因为减速可能来自多个地方。一些可能性:

  • 光线跟踪代码中的某些内容是锁定互斥锁,因此对于大部分光线跟踪运行,一次只允许执行两个线程中的一个

  • 两个线程几乎同时写入相同的内存位置,这会导致缓存争用,从而降低两个线程的执行速度

  • 我的建议是设置线程,使线程1只渲染图像的上半部分,线程2只渲染图像的下半部分;这样,当它们写入输出时,它们将写入内存的不同部分


    如果这没有帮助,您可以暂时用更简单的代码替换渲染代码(例如,只将像素设置为随机值的“渲染器”)查看是否可以看到加速效果。如果可以,则您的
    RayTracer
    实现中可能存在对多线程不友好的情况。

    请记住,这是存在的。您应该测量所花费的时间太长。这是同步或渲染或预处理(准备)或后处理(加入)因此,请在booth单线程和多线程版本中测量代码各部分的持续时间,并对其进行比较。发现不一致的部分是您的问题。这可能很简单,例如像素访问速度慢、锁争用或使用顺序非线程api…还请确保您的线程不会呈现相同的像素…通常是为线程调度整个图像行,因此如果您每
    N+i
    行获得N个内核,则转到
    i-th
    core。根据我的经验,使用像素粒度往往较慢。感谢您的回答。我能够使用像素级粒度实现多线程,现在获得了真正的性能提升。尽管如此,还是出现了一个问题当我为每个线程做相同的工作时,现在仍然在发生(我希望在我更改算法时它会消失),请看以下链接:.pla