Неиспользуемые аргументы по умолчанию уменьшат производительность Переполнение стека

Предположим, я объявил функцию foo(int arg1, int arg2 = 0, int arg3 = 0, int arg4 = 0), Последние три аргумента будут указываться только изредка (если вообще когда-либо), и в основном функция будет называться foo(some_int), Получу ли я производительность, вместо этого объявив функцию как foo(int arg1)И есть другое решение для передачи других аргументов, если они действительно нужны?

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

Функция в данном случае является конструктором для объекта, но это общий вопрос.

5

Решение

(Вы можете просто прочитать заключение в конце, если хотите)

Я сделал тест, чтобы проверить это, я сначала запустил эту короткую программу около десяти раз:

#include <iostream>
#include <ctime>

using namespace std;

int returnMe(int me)
{
return me;
}int main()
{
float begin = (float)clock();
for(int i = 0; i < 100000000; i++)
{
int me = returnMe(i);
}
printf("\nTime: %f\n", begin);
printf("\nTime: %f\n", (float)clock());

return 0;
}

Который в основном выполняет функцию returnMe сто миллионов раз, а потом говорит мне, сколько времени это заняло. Значения варьировались от 280 мс до 318 мс. Затем я запустил эту программу:

#include <iostream>
#include <ctime>

using namespace std;

int returnMe(int me, int me87 = 0, int m8e = 0, int m5e = 0, int m34e = 0,int m1e = 0,int me234 = 0,int me332 = 0,int me43 = 0,int me34 = 0,int me3 = 0,int me2 = 0,int me1 = 0)
{
return me;
}int main()
{
float begin = (float)clock();
for(int i = 0; i < 100000000; i++)
{
int me = returnMe(i);
}
printf("\nTime: %f\n", begin);
printf("\nTime: %f\n", (float)clock());

return 0;
}

примерно в десять раз, и значения теперь находились в диапазоне от 584 мс до 624 мс.

Заключение: Да, это замедлит вызов функции, но на очень небольшую величину. Создание отдельной функции для передачи других аргументов объекту или наличие другого конструктора было бы повышением производительности, но стоило ли бы это дополнительного кода?

Существует еще один способ ее решения, используемый Box2D, который в основном создает отдельную структуру для аргументов по умолчанию и передает указатель на его экземпляр. Таким образом, когда не нужно устанавливать никаких дополнительных аргументов, единственный передаваемый «аргумент мусора», который снижает вашу производительность, — это один нулевой указатель, и это не так уж плохо. Когда вы хотите указать некоторые значения по умолчанию, вы создаете экземпляр указанной структуры в стеке, заполняете нужные значения, а затем передаете их адрес функции. Легко, элегантно и эффективно.

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

========
РЕДАКТИРОВАТЬ: бонус за серьезное тестирование.

Итак, первые два теста были сделаны с простой простой командой компиляции g++ test.cpp -o test.exe, Как указано в многочисленных комментариях, это подразумевает уровень оптимизации -O0. Какие результаты мы получим от тестирования на -O3?

Я повторил тесты, которые сейчас компилируются с g++ test.cpp -o test.exe -O3, но обнаружил, что программа теперь завершается в течение 1-2 мс. Я попытался увеличить количество итераций до одного триллиона, а затем до ста триллионов, тот же результат. Поэтому я подумал, что g ++, вероятно, видел, что я объявлял переменную, которую не собираюсь использовать, и, следовательно, вероятно, пропускал вызовы returnMeи, может быть, весь цикл в целом.

Чтобы получить полезные результаты, я добавил returnMe, чтобы убедиться, что он не был оптимизирован. Вот используемые программы:

#include <iostream>
#include <ctime>

using namespace std;

long long signed int bar = 0;

int returnMe(int me)
{
bar -= me;
return me;
}int main()
{
float begin = (float)clock();
for(int i = 0; i < 1000000000; i++)
{
int me = returnMe(i);
bar -= me * 2;
}
printf("\nTime: %f\n", begin);
printf("\nTime: %f\n", (float)clock());
printf("Bar: %i\n", bar);

return 0;
}

а также

#include <iostream>
#include <ctime>

using namespace std;

long long signed int bar = 0;

int returnMe(int me, int me87 = 0, int m8e = 0, int m5e = 0, int m34e = 0,int m1e = 0,int me234 = 0,int me332 = 0,int me43 = 0,int me34 = 0,int me3 = 0,int me2 = 0,int me1 = 0)
{
bar -= me;
return me;
}

int main()
{
float begin = (float)clock();
for(int i = 0; i < 1000000000; i++)
{
int me = returnMe(i);
bar -= me * 2;
}
printf("\nTime: %f\n", begin);
printf("\nTime: %f\n", (float)clock());
printf("Bar: %i\n", bar);

return 0;
}

Результаты:

Первая программа: от 653 до 686 мс

Вторая программа: от 652 до 735 мс

Как я и ожидал, вторая программа все еще медленнее первой, но сейчас разница менее заметна.

2

Другие решения

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

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

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

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector