11

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

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

+4

Вы должны измерить его. Размер предмета, на который делается ссылка/копирование, - это не единственное, что входит в игру. – juanchopanza

+0

http://stackoverflow.com/questions/21605579/how-true-is-want-speed-pass-by-value –

+0

Вкратце: почти всегда более эффективно передавать собственные типы (int, float, double) на чем по ссылке. Не только потому, что указатель - в большинстве случаев - больше или больше, чем собственный тип данных, но также и потому, что оптимизатору гораздо труднее оптимизировать опорные параметры, чем значения параметров. – MikeMB

ответ

11

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

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

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

Общепринято, например, для того, чтобы машина имела архитектуру (машинные регистры, архитектуру памяти и т. Д.), Что приводит к «сладкому пятну» - копирование переменных какого-то размера является наиболее «эффективным», но копирование большего ИЛИ SMALLER меньше. Большие переменные будут стоить больше, чтобы копировать, потому что может потребоваться сделать несколько копий небольших кусков. Меньшие могут также стоить дороже, потому что компилятор должен скопировать меньшее значение в большую переменную (или зарегистрироваться), выполнить операции над ней, а затем скопировать значение обратно.

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

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

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

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

+2

Вы знаете фактический пример архитектуры, где передача меньшего типа (например, символ) дороже, чем передача большего типа (например, int или указатель)? – MikeMB

+0

Да, хорошо, добавлено несколько примеров. – Peter

+0

Спасибо, но есть ли какие-либо из этих примеров, имеющих отношение к вопросу о прохождении указателя/ссылки против pass by value? В конце концов, речь идет не о прохождении поплавка и прохождение двойника. – MikeMB

3

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

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

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

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

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

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

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


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

+0

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

+0

@space_voyager такой же, как указатель, да. Я добавил редактирование. – user2079303

2

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

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

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

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

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

+1

Обычно это наоборот: когда вы передаете параметр по ссылке/указателю, тогда - на практике - этот параметр всегда должен быть записан в память, тогда как передача его по значению иногда позволяет хранить данные в регистрах. – MikeMB

+0

@MikeMB - это не так в описанном выше сценарии, где оригинальная копия хранится в регистре; передача по значению требует другой копии для сохранения содержимого оригинала, поэтому либо дополнительный регистр должен использоваться, если он доступен, либо вся оптимизация регистра должна быть развернута в памяти из-за слишком небольшого количества регистров. напротив, передача по ссылке может позволить компилятору использовать один и тот же регистр для обеих частей кода (особенно, если функция встроена). Я не утверждаю, что это общий сценарий, но, безусловно, возможно. –

+0

Предполагая, что никакая функция вставки не имеет места. Затем передайте ссылочным способом - по вызывающим соглашениям, о которых я знаю, - указатель на исходное местоположение памяти ** HAS **, который должен быть передан функции, и который требует, чтобы значение фактически хранилось в памяти, в качестве указателя не может указывать на регистр. При передаче по значению вам может потребоваться скопировать значение из одного регистра в другой (не если значение не используется после вызова функции), но вам не нужно хранить его в памяти. – MikeMB

 Смежные вопросы

  • Нет связанных вопросов^_^