Эксплуатационные затраты на передачу по значению по ссылке или указателем?

Рассмотрим объект foo (который может быть int, a double, пользовательский struct, a class, что угодно). Я понимаю, что передача foo ссылкой на функцию (или просто передача указателя на foo) приводит к повышению производительности, поскольку мы избегаем создания локальной копии (что может быть дорого, если foo велико).

Однако, из ответа здесь кажется, что указатели на 64-битной системе можно ожидать на практике иметь размер 8 байтов, независимо от того, что заостренный. В моей системе float - 4 байта. Означает ли это, что если foo имеет тип float, то эффективнее просто передать foo по значению, а не указывать на него указатель (не предполагая никаких других ограничений, которые могли бы сделать используя еще одну эффективную, чем другая внутри функции)?

Ответ 1

Это зависит от того, что вы подразумеваете под "стоимостью" и свойствами хост-системы (аппаратного обеспечения, операционной системы) в отношении операций.

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

Если ваша мера - это скорость выполнения (или "эффективность" ), то игра отличается. Аппаратное обеспечение (и операционные системы и компилятор), как правило, оптимизированы для выполнения операций по копированию данных определенных размеров в силу выделенных цепей (машинных регистров и способов их использования).

Обычно для машины требуется архитектура (машинные регистры, архитектура памяти и т.д.), которые приводят к "сладкому пятну" - копирование переменных какого-то размера наиболее "эффективно", но копирование больших OR Переменные SMALLER меньше. Большие переменные будут стоить больше, чтобы копировать, потому что может потребоваться сделать несколько копий небольших кусков. Меньшие могут также стоить дороже, потому что компилятору необходимо скопировать меньшее значение в большую переменную (или зарегистрироваться), выполнить операции над ней, а затем скопировать значение обратно.

Примеры с плавающей запятой включают некоторые суперкомпьютеры cray, которые изначально поддерживают плавную точку с двойной точностью (aka double в С++), а все операции с одинарной точностью (aka float на С++) эмулируются в программном обеспечении. Некоторые старые 32-разрядные процессоры x86 также работали внутри с 32-битными целыми числами, а для операций с 16-разрядными целыми числами требовалось больше тактовых циклов из-за перевода в/из 32-разрядного (это неверно для более современных 32-разрядных или 64- разрядных процессоров x86, поскольку они позволяют копировать 16-битные целые числа в/из 32-разрядных регистров и работать с ними с меньшим количеством таких штрафов).

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

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

Единственный способ убедиться в том, чтобы измерить его. И поймите, что измерения будут различаться между системами.

Ответ 2

Означает ли это, что если foo имеет тип float, то эффективнее просто передать foo по значению?

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

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

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

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

Есть ли какие-то накладные расходы при использовании ссылки, как это происходит, когда указатель должен быть разыменован?

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


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

Ответ 3

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

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

Считаем, что для передачи float по значению требуется сделать копию float, но при правильных условиях передача float по ссылке может позволить хранить исходный float в регистре с плавающей запятой CPU и рассматривать этот регистр как "reference" к функции. В отличие от этого, если вы передаете копию, компилятор должен найти место для хранения копии, чтобы сохранить содержимое регистра, или, что еще хуже, он вообще не сможет использовать регистр из-за необходимости сохраняя оригинал (это особенно верно в рекурсивных функциях!).

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

Чем больше язык позволяет сосредоточиться на описании того, что вы хотите сделать, а не на том, как вы хотите это сделать, тем больше у компилятора можно найти творческие способы выполнения тяжелой работы для вас. В С++ особенно лучше не беспокоиться о производительности, а вместо этого сосредоточиться на том, чтобы описать, что вы хотите, как можно яснее и проще. Пытаясь описать, как вы хотите выполнить эту работу, вы так же часто мешаете компилятору выполнять свою работу по оптимизации вашего кода для вас.