Контрпродуктивно передавать примитивные типы по ссылке?
Возможный дубликат:
Лучше ли передавать по значению или по ссылке для базовых типов данных?
Причины не передавать простые типы по ссылке?
Я провел некоторое тестирование, где у меня было два сценария, каждый из которых имел две идентичные функции: один передавал параметр по ссылке, а другой - по значению. Сценарий со строками показал значительное увеличение производительности (потому что создается копия строки, вызывая конструктор), тогда как тест с длинным не показывал увеличения производительности при передаче значения по ссылке. Фактически, иногда производительность была хуже.
Ожидается ли это с помощью примитивных типов? Нет ли точки, передающей их по ссылке?
Я ожидал, что копия примитивного типа будет сделана, когда не будет использоваться по ссылке, и поэтому ожидается небольшое повышение производительности.
Ответы
Ответ 1
Вы получаете лучшую производительность от передачи примитивных типов по значению. Это происходит потому, что:
- примитивы неприменимы, поэтому стоимость копии зависит от размера
- примитивы малы, только
double
и long long
больше, чем ссылка в большинстве сред.
- pass-by-value избегает сглаживания, позволяя оптимизатору действительно выполнять свою работу
Эта последняя точка часто упускается из виду, но может иметь существенное значение.
Ответ 2
Да, это ожидаемое поведение. Когда вы передаете параметры по ссылке, вы фактически передаете адрес переменной (например, с указателем). Обычно адрес представляет собой целое число 4 или 8 байтов, поэтому, если ваш примитивный тип не будет больше, вы не получите какого-либо улучшения производительности (и даже если оно больше, вы, вероятно, не будете)
Ответ 3
В С++ ссылка - это просто удобный способ использования указателей. Когда вы используете указатель, вы добавляете дополнительную косвенность. Копирование примитивных типов столь же дешево, как и указатель копирования.
Вот почему примитивные типы передавались по ссылке немного медленнее
Ответ 4
Когда вы передаете значение по ссылке, функция должна разыменовать его для получения значения, и каждый раз, когда вы изменяете значение, возникает разыменование, поскольку вы записываете его в ячейку памяти. Я думаю, что компиляторы могут понять, когда что-то не будет сохранено в исходном местоположении, так что значение будет изменено только на регистрах и будет сохранено обратно, когда это будет необходимо, но я не уверен, насколько это мощно.
Таким образом, существует шаг косвенности, который отсутствует при передаче параметров по значению, что может привести к ухудшению производительности, но это действительно туманно, поскольку оптимизация компилятора возникает. Подумайте о том, что вы передаете указатель, каждый раз, когда вам нужно значение, вы должны извлекать указатель из стека, а затем извлекать указанное значение (так что есть два доступа), тогда как с обычным параметром у вас есть только один.
В любом случае ссылка используется для целей, которые, безусловно, отличаются от производительности.
Ответ 5
Современные компиляторы довольно умны, поэтому, если функция не является "скрытой" (то есть частью чего-то, что компилятор не может видеть во время создания кода), это может вообще не иметь никакого значения. В любом случае, если это компилятор следует вашим инструкциям, передача простых типов в качестве ссылки потенциально может иметь большое значение. В частности, если значение многократно обновляется в коде.
Я увидел код, в котором я работал, что-то вроде этого:
void SomeClass::FindLength(int &len)
{
listEntry* list = theList; // theList is a member variable.
len = 0;
while (list)
{
len++;
list = list->next;
}
}
Изменяя код:
void SomeClass::FindLength(int &len)
{
listEntry* list = theList; // theList is a member variable.
int tempLen = 0;
while (list)
{
tempLen++;
list = list->next;
}
len = tempLen;
}
весь код работал примерно на 30% быстрее и вызывался из множества мест (и я думаю, что в середине было какое-то условие if, поэтому мы не могли просто отслеживать длину). И поскольку это было частью функции API, изменить подпись функции не удалось.
Причина, она была медленнее, с помощью ссылки в том, что компилятор будет писать эталонного значения каждый раз, когда был обновлен, который был груз из памяти в регистр, регистр приращения и хранилища регистра в памяти. С помощью решения tempLen
компилятор мог использовать регистр, который намного быстрее.
Ответ 6
Поскольку вы использовали тег c
, я думаю, вы говорите о указателях (не явных ссылках на С++).
С указателями у вас есть два доступа к памяти: указатель и указанное значение. Таким образом, нет особых выигрышей. Более того, компилятор может сделать больше оптимизаций со значениями: например, нет проблем с псевдонимом.
Ответ 7
Ожидается ли это с помощью примитивных типов?
Я бы сказал абсолютно. У них нет конструкторов, поэтому их не нужно называть.
Нет ли точки, передающей их по ссылке?
Существует: когда вы хотите иметь выходные параметры, то в С++ передача по ссылке считается более эффективной, чем передача указателя.
Я ожидал, что копия примитивного типа будет сделана, когда не будет использоваться по ссылке, и поэтому ожидается небольшое повышение производительности.
Ну, поскольку прохождение по ссылке обычно осуществляется с помощью указателей, тогда компилятор должен испускать код, который толкает что-то в стек, либо значение, либо указатель на значение - и это действительно не имеет значения, какой из них сделан.