Какой из них быстрее? Вызов функции или условное выражение if?

Прежде чем ответить на этот вопрос, рассмотрите также предсказание ветвления.

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

     class Shape
     {
        float Area()
        {
            if(type == SQUARE)
             {
                return length*length;
             }
            else if(type == RECTANGLE)
            {
             return length*breadth;
            }
        } 
     }

Тот же класс можно записать так.

       class Shape
     {
        void SetAreaFunction(void *funcptr)//this function is used to set the current AreaFunc
        {
             CurrentAreaFunc = funcptr ;//this holds the pointer to current area func
        }
        float SqauareArea();//this will return square area
        float RectangleArea();//this will return rectangle area 
        float Area()
        {
            currentAreaFunc();
        } 
     }

ЕСЛИ вы рассматриваете приведенные выше случаи, оба достигают одинаковых результатов. Но я думаю о накладных расходах на производительность. Во втором случае я избегаю проблемы прогнозирования ветвления, вызывая функцию.

Теперь дайте мне знать, что является лучшей практикой и «лучше оптимизированным кодом» в такого рода сценариях. рассмотрите возможность оптимизации моего кода!)

P.S: Я не возражаю, если кто-нибудь даст подробный обзор того, «насколько плохим может быть предсказание ветвления» даже в ассемблерном коде.

Обновление: после профилирования (похожий вид кода выше),
Если условие преуспело в этом виде senario. Может ли кто-нибудь объяснить причину этого? Функциональный код вызова может быть предварительно загружен, так как нет кода ветвления, верно? Но здесь все выглядит иначе... Ветвящийся код выигрывает! :O Профиль для Intel Mac Osx, оптимизация GCC O3/Os.


person Ayyappa    schedule 01.10.2011    source источник
comment
Попробуйте оба и измерьте.   -  person fredoverflow    schedule 01.10.2011
comment
Почему бы не создать тестовый пример и не профилировать его самостоятельно?   -  person Kerrek SB    schedule 01.10.2011
comment
@FredOverflow: этот пример - всего лишь пример. Я хочу знать какие-либо лучшие методы для этого.   -  person Ayyappa    schedule 01.10.2011
comment
@KerrekSB: прогнозирование ветвлений отличается на разных платформах и архитектурах. Я не могу полагаться на свои собственные тестовые примеры.   -  person Ayyappa    schedule 01.10.2011
comment
@Ayyappa: здесь вы действительно близки к области микрооптимизации. Код сначала для правильности, удобочитаемости и ремонтопригодности. Если вы определили эту конкретную функцию как узкое место с помощью профилирования, попробуйте варианты и придерживайтесь той, которая лучше всего работает в этом конкретном месте этой конкретной программы с этим компилятором на этом процессоре.   -  person Mat    schedule 01.10.2011
comment
@Мэт: спасибо. Мне нравятся три слова, которые ты сказал! правильность, удобочитаемость и ремонтопригодность. Кстати, причина этого вопроса заключается в том, чтобы знать наилучшую практику.   -  person Ayyappa    schedule 01.10.2011
comment
@Ayyappa: нет лучшей практики. Этот тип оптимизации действительно зависит от ситуации. Разные процессоры имеют разные затраты на ветвление и непрямой вызов функции. Это не всегда лучше, единственная лучшая практика — профилировать свой код и реагировать в соответствии с этими данными.   -  person Mat    schedule 01.10.2011
comment
Вам может понравиться заявление о том, что преждевременная оптимизация — корень всех зол, но вам нужно понять причину этого. Оптимизация может иметь преимущества, но она также может иметь существенные недостатки с точки зрения удобочитаемости. Очень часто самый эффективный код намного сложнее достаточно эффективного кода.   -  person Jon Skeet    schedule 01.10.2011
comment
@JonSkeet: Да, я согласен. Я знаю, что не могу спорить с вашей точкой зрения, так как она действительно верна. Извините, что я не могу ясно изложить свою точку зрения в вопросе, который я думаю. Обычно я пытаюсь написать более читаемый код, но иногда это не так на некоторых более медленных платформах.   -  person Ayyappa    schedule 01.10.2011
comment
Вы должны использовать else вместо else if. В указателях на функции цель всегда неверно предсказывается.   -  person    schedule 17.06.2012


Ответы (3)


Вы заменили оператор if косвенным.

И ваш оператор if, и косвенное обращение требуют доступа к памяти.

Однако if приведет к короткому переходу, который, вероятно, не сделает конвейер недействительным, в то время как косвенность может сделать конвейер недействительным.

С другой стороны, косвенность — это переход, а оператор if — условный переход. Предсказатель ветвления может пропустить.

Трудно сказать, что быстрее, не протестировав его. Я предсказываю, что оператор if победит.

Пожалуйста, поделитесь своими результатами!

person Lior Kogan    schedule 01.10.2011
comment
Короткий переход не требует сброса текущего предварительно загруженного конвейера даже при сбое ветки ??? - person Ayyappa; 01.10.2011
comment
Как вы сказали, если бы условный переход выиграл гонку. Пожалуйста, не могли бы вы сказать мне разницу между непрямым переходом и условным переходом? - person Ayyappa; 01.10.2011
comment
Безусловный переход всегда можно предсказать, а код можно предварительно выбрать. Для условного перехода он только вероятностный. Вероятность становится лучше для новых архитектур. В последнее время это становится довольно сложным. См. бит -tech.net/hardware/cpus/2008/11/03/ например. - person Lior Kogan; 01.10.2011

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

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

Тем не менее, я помню, как Липпман в своей Объектной модели C++ ссылался на исследование, которое показало, что виртуальные функции (в основном указатели на функции) работают как минимум так же быстро, как переключение типов в реальных приложениях. Я не знаю подробностей, но это где-то в книге.

person sbi    schedule 01.10.2011

Более вероятно, что оптимизатор сможет применить свою магию к оператору if, а не к динамически изменяющемуся указателю на функцию. Просто предположение, теоретически компилятору разрешено делать все, что, как он может доказать, не меняет семантику.

Но в случае, если вы не вызываете функцию, а только реализуете ветвь (используя if в вашем случае), ЦП, скорее всего, применит свою магию, то есть инструкции по изменению порядка , предварительная выборка вещей и тому подобное. Если есть вызов функции между большинством процессоров, то, скорее всего, они "сбросят" свои конвейеры, и оптимизация процессора будет невозможна.

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

Попробуйте сами измерить. Назовите это 1M раз. В C++11 используйте <chrono>, monothonic_clock::now().

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

person towi    schedule 01.10.2011
comment
Я слышал, что сбой на ветке приведет к сбросу предварительно выбранных функций в конвейере. Разве это не правда? Почему процессор не может выполнить предварительную выборку функции, которая не имеет накладных расходов на прогнозирование ветвлений? - Пожалуйста, уточните это, я могу ошибаться в некоторых концепциях. - person Ayyappa; 01.10.2011
comment
@Ayyappa - могло, но это зависит от процессора. Некоторое большое железо может выполнять предварительную выборку сразу нескольких веток параллельно. Однако для этого требуется соответствующая пропускная способность памяти, а у x86, вероятно, ее нет. - person Bo Persson; 01.10.2011
comment
Да, Бо прав. Это сильно зависит от процессора, но я считаю, что x86 может в наши дни. Или, по крайней мере, угадывает и делает прогнозы. Иногда это правильно, иногда неправильно. Таким образом, можно оптимизировать ifs, даже (с профилированием, как указал кто-то другой) - продолжайте и профилируйте свой код! Действительно! Но одно почти наверняка: неудачная (локальная) ветвь не так плоха, как дальний переход (то есть вызов функции). Неудачная ветвь может сделать конвейер недействительным, тогда как дальний прыжок почти наверняка будет. - person towi; 01.10.2011
comment
Спасибо за информацию towi... Я профилировал аналогичный код и обновил результаты в вопросе... - person Ayyappa; 02.10.2011
comment
@Ayy: Хм, нет, мне нужно больше данных. Профилирование означает построчную синхронизацию. Я полагаю, вы сделали общее время, что хорошо для начала. Нет, не могу сказать тебе навскидку. Компилятор? ПРОЦЕССОР? Уровень оптимизации? Построчный тайминг? Хорошим инструментом профилирования, который может дать вам представление, может быть Intel VTune. Я думаю, у него есть пробный период. - person towi; 02.10.2011
comment
@towi: я профилировал его на Intel Mac под управлением OSX (компилятор gcc - оптимизация - O3). У меня было два случая: один для условного и один для вызова функции, каждый из которых выполнялся 10000000 раз. Это дало приблизительную оценку того, что быстрее. - person Ayyappa; 03.10.2011