Требование реального мира я недавно добрался:
Требование A: Реализуйте эту опцию после полного понимания Требования A.
Передача встроенного типа int с помощью const ref на самом деле будет незначительной деоптимизацией (обычно). По крайней мере, для не встроенной функции. Компилятору может потребоваться передать указатель, ссылку на который нужно разорвать, чтобы получить значение. Вы можете подумать, что это всегда можно оптимизировать, но правила псевдонима и необходимость поддержки отдельной компиляции могут заставить компилятор работать.
Однако для вашего второстепенного вопроса:
Для очень маленьких встроенных функций компилятор должен сделать копию целого числа в случае №2? Сообщив компилятору, что мы не будем изменять ссылку, может ли он встроить вызов функции без ненужного копирования целого числа?
Компилятор должен иметь возможность оптимизировать копию или разыменование, если семантика позволяет это; поскольку в этой ситуации компилятор полностью осведомлен о состоянии в месте вызова и реализации функции. Скорее всего, он просто загрузит значение в регистр и просто использует регистр для чего-то еще, когда это будет сделано с параметром. Конечно, все это очень зависит от фактической реализации функции.
Это зависит от компилятора, но я ожидаю, что любой разумный оптимизатор даст вам те же результаты в любом случае.
I протестирован с помощью gcc, и результаты действительно были такими же. вот код, который я тестировал:
inline int foo(const int& n) {
return n * 2;
}
int bar(int x) {
int y = foo(x);
return y;
}
(с и без const & в параметре n foo)
Затем я скомпилировал с gcc 4.0.1 с помощью следующей командной строки:
g++ -O3 -S -o foo.s foo.cc
Результаты двух компиляций были идентичны.
Меня действительно раздражает, когда кто-то использует такие константные ссылки для основных типов данных. Я не вижу в этом никакой пользы, хотя можно утверждать, что для типов данных больше, чем sizeof (pointer)
, это может быть более эффективным. Хотя, мне правда наплевать на такие мелкие «оптимизации».
Обычно это того не стоит. Даже для встроенной функции компилятор не будет глупым. Единственный раз, когда я бы сказал, это уместно, это если у вас есть шаблон;
Многие люди говоря, что между ними нет разницы. Я вижу один (возможно, надуманный) случай, в котором разница будет иметь значение ...
int i = 0;
void f(const int &j)
{
i++;
if (j == 0)
{
// Do something.
}
}
void g()
{
f(i);
}
Но ... Как уже упоминали другие ... целые числа и указатели, вероятно, будут иметь одинаковый размер. Для чего-то такого маленького, как целое число, ссылки снизят вашу производительность. Наверное, победил Это будет тоже заметно, если ваш метод не вызывается часто, но он будет там. С другой стороны, при некоторых обстоятельствах компилятор может его оптимизировать.
При написании или использовании шаблонов вы можете получить (const int &), потому что автор шаблона не может знать, что это за тип на самом деле. Если объект тяжелый, то правильным решением будет передача ссылки; если это int или что-то в этом роде, компилятор может его оптимизировать.
В отсутствие каких-либо внешних требований, как правило, нет причин делать что-то подобное для одноразовой функции - это просто лишний набор текста и перебрасывание ссылок на самом деле препятствуют оптимизации. Копирование небольших данных в регистры намного дешевле, чем их перезагрузка из памяти в случае их изменения!
Я не могу придумать никакой выгоды. Я даже видел рекомендацию, что при написании шаблонов вы должны использовать метапрограммирование для передачи целых типов по значению и использовать ссылку на константу только для нецелочисленных типов.
Вы можете использовать boost :: call_traits
для оптимальной передачи параметров. По умолчанию используется простая передача параметров примитивных типов и передача по константной ссылке структур и классов.
ну, стоимость ссылки обычно такая же, как и для целочисленного типа, но со ссылкой у вас есть косвенное обращение, которое должно иметь место, потому что ссылка на некоторую память должна быть разрешается в значение.
Просто копируйте по значению, придерживаясь неизменного соглашения для встроенных типов.
Дело не только в производительности. Правдивая история: на этой неделе я заметил, что коллега попытался улучшить числовые рецепты и заменил макрос
#define SHFT(a,b,c,d) do { (a)=(b); (b)=(c); (c)=(d); } while (0)
этой функцией
inline void Rotate(double& dFirst, double& dSecond, double& dThird, const double dNewValue)
{
dFirst = dSecond;
dSecond = dThird;
dThird = dNewValue;
} // Function Rotate
. Это сработало бы, если бы он передал последний параметр по ссылке, но так как он есть этот код
Rotate(dum,*fb,*fa,dum);
, который должен был поменять местами * fa и * fb, больше не работает. Передача его по ссылке без const невозможна, так как в других местах последнему параметру передаются не-l-значения.
Не делайте этого. int
имеет тот же размер, что и указатель / ссылка на обычных 32-битных платформах, и меньше на 64-битных, таким образом, вы можете получить снижение производительности вместо преимущества. Я имею в виду, что все аргументы функции помещаются в стек по порядку, чтобы функция могла их прочитать, и это будет либо ваш int, либо его адрес в случае ссылки. Другой недостаток заключается в том, что вызываемый будет либо обращаться к вашему n
через косвенное обращение (разыменование адреса), либо сделает копию в своем стеке для оптимизации.
Если вы внесете некоторые изменения в переданный int по значению он может быть записан либо обратно в то место в стеке, где он был передан, либо в новую позицию в стеке. Второй случай, естественно, не выгоден, но не должно быть.
Прочтите Хотите скорости? Пропустить по значению Дэйва Абрахамса.