Оптимизация компилятора C ++ с сокращенной IF - PullRequest
1 голос
/ 23 февраля 2012

Я имею в виду этот вопрос относительно оптимизации компилятора, если речь идет о сокращенном, если / еще.

У меня есть эта функция:

double eu_distance (const coor& x, const coor& y) {

return ((y.x - x.x)*(y.x - x.x) + (y.y - x.y)*(y.y - x.y));
}

Мне интересно, что эффективнее?

min = min > eucl_distance(point_a, point_b) ? eucl_distance(point_a, point_b) : min;

или

double dis = eucl_distance(point_a, point_b);
if (min > dis)
    min = dis;

в первом случае, знает ли компилятор (в моем случае, GCC 4.6.2), как оптимизировать то, что если / else сохранить возвращаемое значение eucl_distance (), использовать его повторно, а не вычислять его дважды?

Вопрос с ответом будет:

Что эффективнее?

(y.x - x.x)*(y.x - x.x)

или

pow((y.x - x.x),2)

PS: Извините, что я не могу выбрать более одного правильного ответа !! :( Спасибо всем за ваши ответы! Я действительно ценю их !!

Ответы [ 4 ]

5 голосов
/ 23 февраля 2012

Универсального ответа нет: вам придется профилировать код, сгенерированный вашей реализацией, чтобы знать наверняка.Однако в большинстве случаев, если eu_distance находится в отдельной единице перевода и специально не аннотирован, компилятор не сможет знать, что его повторный вызов с одинаковыми аргументами даст одинаковые результаты;в этом случае вторая форма почти наверняка будет быстрее.С другой стороны, если eu_distance может быть встроен, любой приличный оптимизатор в конечном итоге будет генерировать практически одинаковый код для обоих.

На практике я почти наверняка использовал бы третью форму:

min = std::min( eu_distance( point_a, point_b ), min );

(я полагаю, что eucl_distance - это опечатка для eu_distance.)

Кроме того, я бы избегал такого имени, как min.Кто-то слишком вероятно может добавить using namespace std; позже или даже включить <windows.h>, без определения NOMINMAX.(<windows.h> определяет min и max как макросы, если NOMINMAX не был определен. Это приводит к некоторым интересным сообщениям об ошибках, если вы определяете свои собственные min или max. Или даже включаете <algorithm>.)

Относительно pow( x, 2 ): опять же, вам действительно придется измерять, но обычно x * x будет быстрее, даже если x - сложное выражение.(Конечно, если выражение нетривиально, то признание того, что оба x являются идентичными, может оказаться не таким простым, что затрудняет чтение кода. В таких случаях вам может потребоваться рассмотреть небольшую функцию, скажем squared, который ничего не делает, кроме как возвращает x * x. Вставьте его, если это влияет на производительность.)

2 голосов
/ 23 февраля 2012

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

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

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

1 голос
/ 23 февраля 2012

Ну, в теории это:

double dis = eucl_distance(point_a, point_b);
if (min > dis)
    min = dis;

более эффективно, чем это:

min = min > eucl_distance(point_a, point_b) ? eucl_distance(point_a, point_b) : min;

... только потому, что вы исключаете дополнительный вызов eucl_distance(point_a, point_b).

Но если вы пометите вашу функцию как «чистую» или если компилятор сможет увидеть определение и решить, что оно чистое, он исключит дополнительный вызов к нему так же, как удалит дублирующиеся математические выражения, а затем сгенерирует код тот же самый. Однако вы не всегда можете проверить сборку и т. Д., Поэтому я остановлюсь на первом случае, который чище.

Что касается вашего дополнительного вопроса, (y.x - x.x)*(y.x - x.x) быстрее (вы можете быть более точным и сохранить результат y - x в переменной tmp) ... Однако компилятор хорошо знает функцию pow, и поскольку второй аргумент Выражение времени компиляции позволяет легко развернуть этот код в соответствии с вашим умножением, закодированным вручную. Но опять же, вы не можете действительно полагаться на себя, поэтому, если вы можете сохранить код чище и более наглядным - сделайте это. Я не вижу причин, почему вы хотели бы позвонить, например, pow(something, 2).

И всегда помните, что преждевременная оптимизация не годится. Но, тем не менее, сначала не пишите дерьмовый медленный код:)

0 голосов
/ 23 февраля 2012

я бы сказал:

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

ничто не говорит компилятору о том, что функция eucl_distance полностью idempotent.so нет никаких шансов пропустить дополнительный вызов при условии кэширования результата.(что, если вы увеличите статическую переменную-счетчик в вашем вызове ???)

для умножения против пау, я думаю, это зависит от математической оптимизации базового процессора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...