Оптимизация проверки выровненной оси с выравниванием по оси

В настоящее время я работаю с pointclouds alot, и я реализовал алгоритм сегментации, который кластеры указывают с определенным максимальным расстоянием в сегменты.

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

Я запускаю свою программу через gnuprof, и это результат:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls   s/call   s/call  name    
 52.42      5.14     5.14 208995661     0.00     0.00  otree_node_out_of_bounds
 19.60      7.06     1.92 189594292     0.00     0.00  otree_has_point_in_range
 11.33      8.17     1.11   405834     0.00     0.00  otree_node_has_point_in_range
  9.29      9.08     0.91   352273     0.00     0.00  find_matching_segments
 [...]

Как вы можете видеть, большая часть времени вычислений проводится в otree_node_out_of_bounds , который реализован следующим образом:

int otree_node_out_of_bounds(struct otree_node *t, void *p)
{
    vec3 *_p = p;
    return (_p->x < t->_llf[0] - SEGMENTATION_DIST 
        || _p->x > t->_urb[0] + SEGMENTATION_DIST
        || _p->y < t->_llf[1] - SEGMENTATION_DIST 
        || _p->y > t->_urb[1] + SEGMENTATION_DIST
        || _p->z < t->_llf[2] - SEGMENTATION_DIST 
        || _p->z > t->_urb[2] + SEGMENTATION_DIST);
}

где SEGMENTATION DIST - это константа времени компиляции, позволяющая gcc делать некоторую постоянную складку. _llf и _urb имеют тип float [3] и представляют собой ограничительную рамку октета.

Итак, мой вопрос в основном заключается в том, можно ли сделать какую-то скрытую оптимизацию для этой функции или, что более общее, есть более эффективный способ проверки границ на AABB или даже по-разному выражать ее, могу ли я ускорить сравнение каким-то образом с помощью некоторой магии C/gcc?

Если вам нужна дополнительная информация, чтобы ответить на этот вопрос, пожалуйста, дайте мне знать :)

Благодаря, Энди.

1
nl ja de

2 ответы

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

Ваша функция выглядит отлично, как написано. Я сомневаюсь, что вы могли бы оптимизировать индивидуальный тест, подобный этому, чем у вас (или, если можно, это не будет драматичным). Если вы хотите оптимизировать всю операцию, вам нужно сделать это на более высоком уровне:

  • Вы можете попробовать другую структуру (например, kd-tree вместо octree) или совершенно новый алгоритм, который использует некоторые шаблоны в ваших данных.
  • Вы можете инвертировать цикл из «для каждой точки проверки соответствия» на «для каждой контрольной точки otree», что позволяет повторно использовать данные границ снова и снова.
  • Вы можете обеспечить доступ к данным (точках, возможно) наиболее эффективным способом (т. е. последовательно, а не случайным образом прыгать).
  • С перестроенным циклом вы можете использовать SSE для выполнения нескольких тестов границ в одной команде (без ветвления!).
2
добавлено
Спасибо за ваши предложения. У меня есть несколько заметок для каждого из них: 1.) Я думал об использовании kdtrees, на самом деле, я просто не смог их реализовать :) 2.) Мы говорим о потенциально миллиардах пунктов здесь, поэтому я сомневаюсь в обратном цикл будет быстрее. 3.) Я делаю это, я думаю, 4.) Я этого не понимал, но это звучит интересно. можете ли вы предоставить более подробную информацию? Sidenote: я не думаю, что профилировщик перевычисляет стоимость этой функции, так как более 50% моей всей среды исполнения там расходуется :)
добавлено автор Andreas Grapentin, источник
Я включил и перепрофилировал программу, как вы предложили, и влияние функции на производительность действительно сократилось, что можно было ожидать из-за удаленных вызовов функций и их возврата. Однако в этой функции еще много времени.
добавлено автор Andreas Grapentin, источник
Я много думал о рассматриваемом алгоритме, и текущее состояние программы действительно намного быстрее, чем мой первоначальный подход. Но я думаю, что нажимаю на нее стену. Я не могу придумать интуитивный подход к улучшению общих вычислений. Тем не менее, я ценю ваши указатели и буду рассматривать то, что вы предложили. Еще раз спасибо :)
добавлено автор Andreas Grapentin, источник
Кроме того, я вознагражу вас щедростью, как только она будет разблокирована.
добавлено автор Andreas Grapentin, источник
@AndreasGrapentin: Как я уже сказал в своем ответе, наиболее эффективным способом ускорить ваш результат будет то, чтобы избежать как можно большего количества из этих сравнений «в границах», используя другую структуру данных. В противном случае реорганизация цикла может иметь огромное влияние на производительность. Без полного описания проблемы (для предложений алгоритма) или кода для иерархии над вашей функцией (для структуры цикла и предложений SSE) трудно быть более конкретным.
добавлено автор Ben Jackson, источник
@AndreasGrapentin: Кроме того, я был точно , где у вас возникла проблема, когда я вручную оптимизировал внутренний тест и получил 10% -ное ускорение, а затем понял, что алгоритм, вызывающий его, был случайно O (n ) вместо O (log n) из-за ошибочных предположений. Когда я исправил это, вся программа (включая множество других операций) завершила 27 раз быстрее.
добавлено автор Ben Jackson, источник

Мне это хорошо. Единственная микро оптимизация, о которой я могу думать, объявляет * _p как статическую

1
добавлено
Спасибо за предложение. Однако, когда я объявляю _p как static vec3 * _p , gcc сообщает мне otree.c: In function 'otree_node_out_of_bounds': otree.c: 74: 2: error: элемент initializer не является константой `
добавлено автор Andreas Grapentin, источник
Извини, я виноват. ваше предложение работает, как и ожидалось, я проверю профайлер :)
добавлено автор Andreas Grapentin, источник
ничего себе, это на самом деле немного быстрее. Но почему?
добавлено автор Andreas Grapentin, источник
Извини, мой плохой снова. У вас нет выигрыша от производительности :)
добавлено автор Andreas Grapentin, источник