Существуют ли параметры компилятора C ++, которые позволяют агрессивно удалять все вызовы функций и передавать их в аргументы функциям с пустыми телами? - PullRequest
1 голос
/ 21 февраля 2020
#include <iostream>

int ExpensiveFunction() {
    // Do expensive calculation
    // ...

    std::cout << "You shouldn't see this!" << std::endl;

    return 42;
}

void print(int num)
{

}

int main()
{
    print(ExpensiveFunction());
    return 0;
}

Мне бы хотелось, чтобы компилятор распознал, что print - пустая функция, поэтому полностью удалите вызов print и оценку аргумента оператора:

print(ExpensiveFunction());

Однако независимо от того, какие настройки оптимизации Я использую (с MSV C), он всегда будет выполнять вычисления дорогостоящей функции.

Я признаю, что оптимизация, которая удаляет вызов ExорогоFunction, может быть деструктивной оптимизацией, но я чувствую, что это должно быть возможно без преобразования print в макрос, который будет скомпилирован в наиболее оптимизированной конфигурации.

Можно ли вообще заставить любой компилятор C ++ сделать это?

EDIT: Я добавил оператор std :: cout

Ответы [ 3 ]

3 голосов
/ 21 февраля 2020

Для G CC и Clang вы можете использовать __attribute__((pure)), чтобы заверить компилятор, что ExpensiveFunction() не имеет побочных эффектов и может быть исключен, если его результат не используется. В некоторых простых случаях эти компиляторы могут в любом случае выяснить чистоту функции, но использование атрибута позволяет получить этот эффект в более сложных случаях и для функций, определенных в других единицах перевода.

К сожалению, MSV C не имеет эквивалентного объекта, о котором я знаю. См. атрибуты функции pure / const в различных компиляторах для получения дополнительной информации.

3 голосов
/ 21 февраля 2020

Можно ли вообще заставить любой компилятор C ++ сделать это?

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

Оба Clang и G CC (для последних протестированных версий), сделайте это со стандартным флагом -O3. На самом деле, они даже делают это с -O1 и -O2 ... вы должны полностью отключить оптимизацию, чтобы они отправили вызов на ExpensiveFunction.

Быстрая попытка с x64 MSV C 19.24 показывает, что он также оптимизирует вызов, по крайней мере с /O2.

MSV C перечислены параметры командной строки здесь .

G CC параметры командной строки приведены в руководстве, параметры оптимизации c здесь указаны .

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


Обратите внимание, что это только тестирование вашего примера кода - если ExpensiveFunction имеет наблюдаемые побочные эффекты, оптимизатор не сможет удалить его.


Редактировать , поэтому получается, что ExpensiveFunction имеет наблюдаемые побочные эффекты.

Я понимаю, что Оптимизация, которая удаляет вызов ExорогоFunction, может быть деструктивной оптимизацией,

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

Давайте рассмотрим Ваш заголовок, исправленный в соответствии с основной частью вопроса:

Существуют ли параметры компилятора C ++, которые позволяют агрессивно удалять все вызовы функций и передаваться в аргументах функциям с непустыми тел?

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

int main() {}

и назвать ее «разрушительной оптимизацией». Это правильное решение для вашего исправленного заголовка, верно?

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

Можно также сделать условное тело ExpensiveFunction условным, если, например, это действительно отладочный вывод, наблюдаемый побочный эффект которого вы хотите отключить в некоторых сборках

int ExpensiveFunction() {
    #ifndef DEBUG
    // Do expensive calculation
    // ...

    std::cout << "You shouldn't see this!" << std::endl;
    #endif
    return 42;
}

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

1 голос
/ 21 февраля 2020

Для MSV C вам понадобятся /O2 (оптимизация), /GL + /LTCG (генерация кода времени соединения) и /OPT:REF (исключение функций и данных, на которые никогда не ссылаются).

...