不完整的多线程RayTracer需要的时间是预期的两倍

时间:2018-05-01 23:47:24

标签: c++ multithreading graphics parallel-processing raytracing

我正在制作MT Ray-Tracer多线程,正如标题所说,它的执行时间是单线程版本的两倍。显然目的是将渲染时间缩短一半,但我现在正在做的只是发送光线跟踪方法运行两次,每个线程一次,基本上执行两次相同的渲染。尽管如此,由于线程可以并行运行,因此执行时间不应该有意义地增加。但是要加倍。

这必须与我的多线程设置有关。我认为它与我将它们创建为可连接的事实有关。所以我将解释我在做什么,并且还会提供相关代码,看看是否有人可以确认这是否是问题。

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

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

 pthread_exit (NULL); 

作为最后一件事(它上面唯一的MT)。然后再做一个循环来加入线程。最后我明星在一个小循环中编写文件。最后关闭文件并删除与用于在draw方法中存储图像的数组相关的指针。

我可能不需要使用加入现在我没有做一个“真正的”多线程光线跟踪器,只是渲染两次,但是一旦我开始在图像像素之间交替(即,thread0 - >渲染) pixel0 - thread0 - >存储pixel0,thread1 - >渲染pixel1 - thread1 - >存储pixel1,thread0 - >渲染pixel2 - thread0 - >存储pixel2 ,, thread1 - >渲染pixel3 - thread1 - >存储pixel3等...)我想我需要它才能在文件上以正确的顺序写入像素。

这是对的吗?我真的需要在这里使用我的方法(或与其他任何人一起使用?)。如果我这样做,我如何发送线程并发运行,而不是等待对方完成?这个问题与加入完全无关吗?

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 (); 
}

1 个答案:

答案 0 :(得分:1)

你确实想加入()线程,因为如果不这样做,你就会遇到一些问题:

  1. 你怎么知道线程何时完成执行?您不想开始写出生成的图像,只是为了发现在您编写图像时没有完全计算出来。

  2. 您如何知道何时可以安全地删除线程可能正在访问的任何数据结构?例如,您的RayTracer对象位于堆栈中,而(AFAICT)您的线程正在写入其像素阵列。如果你的main函数在线程退出之前返回,那么线程很可能最终会写入不再存在的RayTracer对象,这会通过覆盖其他任何对象来破坏堆栈。在你的函数返回后,在那些相同的位置存在(通过偶然)。

  3. 所以你肯定需要加入()你的线程;但是,您不需要将它们显式声明为PTHREAD_CREATE_JOINABLE,因为无论如何都已默认设置该属性。

    加入线程不应该导致线程速度变慢,只要两个线程都创建并在之前运行,就可以在其中任何一个上调用join()(这在你的情况下似乎就是这种情况)发布代码)。

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

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

    2. 两个线程几乎同时写入相同的内存位置,这导致缓存争用,这会减慢两个线程的执行速度。

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

      如果这没有帮助,您可以暂时用更简单的东西替换渲染代码(例如,将像素设置为随机值的“渲染器”),以查看是否可以看到加速。如果是这样,那么RayTracer的实现中可能会有一些不符合多线程的内容。