Есть идеи, почему новый код замедляет трассировку лучей?

c++ performance raytracing

102 просмотра

3 ответа

79 Репутация автора

Я делаю трассировщик лучей, и я реализовал коррекцию перспективы, вычисляя положения лучей, которые должны быть запущены, используя текущие значения пикселей по осям x и y для вычисления текущего направления луча. Вот кусок кода:

float fov = 60; 
float invWidth = 1/float(image.getWidth());
float invHeight = 1/float(image.getHeight());
float angle = (fov * M_PI * 0.5/180 );
float aspectratio = image.getWidth()/float(image.getHeight());
point camera = scene.getCamera();
for (int y=0;  y<image.getHeight(); y++) { 
  for (int x=0; x<image.getWidth(); x++) {
    ......
    ......
    float xx = (((x*invWidth) *2)-1) * angle * aspectratio; 
    float yy = (((y*invHeight)*2)-1) * angle;
    Ray viewRay = { {camera.x, camera.y, camera.z}, {xx, yy, 1.0f}}; 

Пока все хорошо, отлично работает. Однако я понял, что значения xx и yy (направление пикселей) не нужно рассчитывать для каждого пикселя, только пару раз равных ширине и длине изображения. Поэтому я переписал эти части следующим образом:

float fov = 60; 
float invWidth = 1/float(image.getWidth());
float invHeight = 1/float(image.getHeight());
float angle = (fov * M_PI * 0.5/180 );
float aspectratio = image.getWidth()/float(image.getHeight());
float rays_x [image.getWidth()], rays_y [image.getHeight()];
for (int y=0; y<image.getHeight(); y++)
    rays_y [y] = (((y*invHeight)*2)-1) * angle;
for (int x=0; x<image.getWidth(); x++)
    rays_x [x] = (((x*invWidth) *2)-1) * angle * aspectratio;
point camera = scene.getCamera();
for (int y=0;  y<image.getHeight(); y++) { 
  float yy = rays_y[y];
  for (int x=0; x<image.getWidth(); x++) {
    ......
    ......
    Ray viewRay = { {camera.x, camera.y, camera.z}, {rays_x[x], yy, 1.0f}};

Я в основном предварительно вычислил направления лучей и сохранил их в массивах. Я ожидал небольших улучшений в производительности, возможно, ничего в пессимистичном случае, но я никогда не ожидал, что это получится ХОРОШИМ. Раньше я занимал 1.67s для рендеринга сцены, а теперь требуется 1.74! Не масштабное падение, но удивительное видение, что я ожидал, что теперь буду выполнять намного меньше работы. Я отключил оптимизацию компилятора (-O3 и -ffast-math) и протестировал с двумя подходами. Раньше проходило между 9.03 и 9.05, а теперь проходит между 9.06 и 9.15.

Так как же мне исследовать этот вопрос? Единственное, что пришло мне в голову, - это меньше обращений к кэшу из-за доступа ray_x [x] при каждой итерации цикла и ray_y [y] через каждые 1024 итерации, хотя я бы никогда этого не подозревал, потому что это всего лишь 1024 * 4 = 4096 + (768 * 4) = 7168 байт в сумме. Любые идеи будут оценены.

Автор: user2752471 Источник Размещён: 18.07.2016 08:22

Ответы (3)


0 плюса

1313 Репутация автора

Компилятор поймет, что это:

float yy = (((y*invHeight)*2)-1) * angle;

постоянные данные, и их нужно рассчитывать только один раз за цикл.

Следовательно, ваш предварительно вычисленный год - это пустая трата производительности.

Предварительно вычисленные значения xx могут помочь, но если выражение содержит много постоянных данных (то есть invWidth * 2 и angle * aspectratio ), производительность может не увеличиться и даже ухудшиться из-за ошибок в кеше.

float xx = (((x*invWidth) *2)-1) * angle * aspectratio; 
Автор: Sven Nilsson Размещён: 18.07.2016 08:33

0 плюса

766 Репутация автора

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

Что-то вроде этого:

float *rays_x, *rays_y;

void compute_directions()
{
    rays_x = new float[image.getWidth()];
    rays_y = new float[image.getHeight()];
    for (int y=0; y<image.getHeight(); y++)
        rays_y[y] = (((y*invHeight)*2)-1) * angle;
    for (int x=0; x<image.getWidth(); x++)
        rays_x[x] = (((x*invWidth) *2)-1) * angle * aspectratio;
}

void render()
{
    float fov = 60; 
    float invWidth = 1/float(image.getWidth());
    float invHeight = 1/float(image.getHeight());
    float angle = (fov * M_PI * 0.5/180 );
    float aspectratio = image.getWidth()/float(image.getHeight());
    point camera = scene.getCamera();
    for (int y=0;  y<image.getHeight(); y++) { 
    float yy = rays_y[y];
    for (int x=0; x<image.getWidth(); x++) {
        ......
        ......

Очевидно, вам нужно переместить угол и аспектное отношение куда-то еще, чтобы вы могли получить к ним доступ в compute_directions. Также не забудьте удалить свои указатели с помощью delete [], если они вам больше не нужны, чтобы предотвратить утечку из памяти.

Автор: Mario Dekena Размещён: 18.07.2016 08:36

0 плюса

5926 Репутация автора

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

Некоторые основные правила по оптимизации:

  • Прежде чем пытаться что-то оптимизировать: профиль.
  • После оптимизации чего-либо: профиль.

Вы не можете ожидать какого-либо прироста производительности от оптимизации, прежде чем узнаете, где ваша программа на самом деле тратит время.

В Linux вы можете использовать GCCs -pg и gprof . Вы также можете использовать perf и valgrind (например, callgrind, чтобы получить представление о количестве вызовов конкретной функции).

Также проверьте Perf Wiki .

Автор: Marcus Borkenhagen Размещён: 01.08.2016 07:37
Вопросы из категории :
32x32