Какие затраты возникают при использовании Cell <T>, а не только T? - PullRequest
4 голосов
/ 14 марта 2019

Я наткнулся на комментарий на reddit , который указывает, что использование Cell<T> предотвращает определенные оптимизации:

Ячейка работает без дополнительной памяти (ячейка имеет тот же размер, что и T), и с небольшими накладными расходами во время выполнения (она «просто» запрещает оптимизацию, она не вводит дополнительных явных операций)

Это, кажется, противоречит другим вещам, которые я читал о Cell<T>, в частности, что это "нулевая стоимость". Первое место, с которым я столкнулся с этой категорией, - здесь .

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

1 Ответ

9 голосов
/ 14 марта 2019

TL; DR Cell - Абстракция с нулевыми накладными расходами ; то есть те же функциональные возможности, реализованные вручную, имеют одинаковую стоимость.


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

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

Чтобы добавить в заблуждение, механизм исключений, используемый большинством реализаций C ++ и который Rust использует для panic = unwind, называется Исключениями с нулевой стоимостью и подразумевает 1 , чтобы не добавлять накладные расходы на метательный путь. Это другой вид нулевой стоимости ...

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

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


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

На стороне памяти нет служебных данных:

  • sizeof::<Cell<T>>() == sizeof::<T>()
  • с учетом cell типа Cell<T>, &cell == cell.as_ptr().

(Вы можете посмотреть в исходном коде )

На стороне доступа Cell<T> требует затрат времени выполнения по сравнению с T; стоимость дополнительного функционала.

Самая непосредственная цена состоит в том, что манипулирование значением с помощью &Cell<T> требует копирования его туда и обратно 1 . Это побитовая копия, поэтому оптимизатор может исключить ее, если сможет доказать, что это безопасно.

Другая заметная стоимость заключается в том, что UnsafeCell<T>, на котором основан Cell<T>, нарушает правила, которые &T означают, что T нельзя изменить.

Когда компилятор может доказать, что часть памяти не может быть изменена, он может оптимизировать дальнейшие операции чтения: прочитайте t.foo в регистре, затем используйте значение регистра вместо того, чтобы снова читать t.foo.

В традиционном коде Rust &T дает такую ​​гарантию: независимо от того, есть ли непрозрачные вызовы функций, вызовы кода C и т. Д. ... между двумя чтениями до t.foo, второе чтение вернет одно и то же значение как первое гарантировано. С &Cell<T> такая гарантия больше не существует, и, таким образом, если оптимизатор не докажет без сомнения, что значение является неизменным 2 , он не сможет применять такие оптимизации.

1 Вы можете бесплатно манипулировать значением с помощью &mut Cell<T> или с помощью unsafe кода.

2 Например, если оптимизатор знает, что значение находится в стеке, и никогда не передавал адрес значения кому-либо еще, то он может сделать разумный вывод, что никто другой может изменить значение. Хотя атака с разбиванием стека может, конечно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...