Неполная многопоточность RayTracer занимает вдвое больше времени, чем ожидалось - PullRequest
0 голосов
/ 02 мая 2018

Я делаю многопоточность MT Ray-Tracer, и, как говорится в заголовке, для его выполнения требуется вдвое больше, чем для однопоточной версии. Очевидно, что цель состоит в том, чтобы сократить время рендеринга вдвое, однако сейчас я просто отправляю метод трассировки лучей дважды, по одному для каждого потока, в основном выполняя один и тот же рендеринг дважды. Тем не менее, поскольку потоки могут работать параллельно, не должно быть значительного увеличения времени выполнения. Но о удвоении.

Это должно быть связано с моей настройкой многопоточности. Я думаю, это связано с тем, что я создаю их как объединяемые. Поэтому я собираюсь объяснить, что я делаю, а также добавить соответствующий код, чтобы посмотреть, сможет ли кто-нибудь подтвердить, что это проблема.

Я создаю два потока и устанавливаю их как объединяемые. Создайте RayTracer, который выделяет достаточно памяти для хранения пикселей изображения (это делается в конструкторе). Запустите цикл из двух итераций для отправки соответствующей информации для каждого потока, например, идентификатора потока и адреса экземпляра 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 и т. д.) I думаю, мне это понадобится, чтобы можно было писать пиксели в правильном порядке в файле.

Это правильно? Мне действительно нужно использовать соединение здесь с моим методом (или с любым другим?). Если я это сделаю, как я могу отправить потоки для одновременной работы, не дожидаясь завершения других? Проблема совершенно не связана с присоединением?

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 голосов
/ 02 мая 2018

Вы хотите присоединиться () к потокам, потому что если вы этого не сделаете, у вас есть несколько проблем:

  1. Как узнать, когда потоки закончили выполняться? Вы не хотите начинать записывать получившееся изображение только для того, чтобы обнаружить, что оно не было полностью рассчитано на момент его написания.

  2. Как вы узнаете, когда безопасно разрушать любые структуры данных, к которым могут обращаться потоки? Например, ваш RayTracer объект находится в стеке, и (AFAICT) ваши потоки записывают в его пиксельный массив. Если ваша основная функция возвращается до выхода из потоков, есть очень хороший шанс, что потоки будут иногда заканчивать запись в RayTracer объект, который больше не существует, который повредит стек, перезаписывая любые другие объекты, которые могут существовать ( по случайности) в тех же местах после возврата вашей функции.

Так что вам определенно нужно присоединиться () к вашим темам; вам не нужно явно объявлять их как PTHREAD_CREATE_JOINABLE, так как этот атрибут в любом случае уже установлен по умолчанию.

Присоединение к потокам не должно приводить к замедлению потоков, если оба потока созданы и запущены до , когда вы вызываете join () для любого из них (что, похоже, относится к опубликованным вами сообщениям). код).

Что касается того, почему вы видите замедление с двумя потоками, трудно сказать, так как замедление может происходить из ряда мест. Некоторые возможности:

  1. Что-то в вашем коде трассировки лучей блокирует мьютекс, так что для большей части прогона трассировки лучей в любом случае разрешено выполнять только один из двух потоков.

  2. Оба потока записывают в одни и те же области памяти примерно в одно и то же время, что приводит к конфликту в кэше, что замедляет выполнение обоих потоков.

Я бы предложил настроить ваши потоки так, чтобы поток № 1 отображал только верхнюю половину изображения, а поток № 2 - только нижнюю половину изображения; таким образом, когда они пишут свой вывод, они будут записывать в разные разделы памяти.

Если это не поможет, вы можете временно заменить код рендеринга на что-то более простое (например, «рендер», который просто устанавливает пиксели в случайные значения), чтобы увидеть, сможете ли вы увидеть ускорение с этим. Если это так, то в вашей реализации RayTracer может быть что-то, что не поддерживает многопоточность.

...