Есть ли какие-либо накладные расходы на чтение только для чтения? - PullRequest
18 голосов
/ 27 мая 2009

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

Возможно, я предположил это, потому что я не знал, что поле readonly можно инициализировать только внутри конструктора или внутри самого объявления поля, и без проверки во время выполнения вы не сможете гарантировать, что оно не будет назначается несколько раз различными способами. Но теперь я знаю это, это может легко быть статически проверено компилятором C #, верно? Так это так?

Другая причина состоит в том, что я читал, что использование readonly оказывает «незначительное» влияние на производительность, но они никогда не входили в это утверждение, и я не могу найти информацию по этому вопросу, поэтому мой вопрос. Я не знаю, какое еще влияние на производительность может оказать, кроме проверки во время выполнения.

Третья причина состоит в том, что я увидел, что readonly сохраняется в скомпилированном IL как initonly, поэтому, почему эта информация должна быть в IL, если readonly является не чем иным, как гарантией со стороны Компилятор C #, которому поле никогда не назначается за пределами конструктора или объявления?

С другой стороны, я обнаружил, что вы можете установить значение readonly int путем отражения без CLR, выдающего исключение, что не должно быть возможно, если readonly была проверка во время выполнения.

Так что я думаю: «только чтение» - это функция времени компиляции, кто-нибудь может подтвердить или опровергнуть это? И если это так, что является причиной для включения этой информации в IL?

Ответы [ 4 ]

16 голосов
/ 27 мая 2009

Вы должны смотреть на это с той же точки зрения, что и модификаторы доступа. Модификаторы доступа существуют в IL, но действительно ли они проверяются во время выполнения? (1) я не могу напрямую назначить приватные поля во время компиляции, (2) я могу назначить их, используя отражение. Пока что это не проверка во время выполнения, как readonly .

Но давайте рассмотрим модификаторы доступа. Сделайте следующее:

  1. Создать сборку A.dll с открытым классом C
  2. Создайте сборку B.exe, которая ссылается на A.dll. B.exe использует класс C.
  3. Сборка двух сборок. Запуск B.exe работает просто отлично.
  4. Перестроить A.dll, но установить класс C для внутреннего. Замените A.dll в каталоге B.exe.

Теперь при запуске B.exe возникает исключение времени выполнения.

Модификаторы доступа также существуют в IL, верно? Так какова их цель? Цель состоит в том, чтобы другие сборки, которые ссылаются на сборку .Net, должны были знать, к чему им разрешен доступ и к чему они не имеют доступа, как во время компиляции, так и во время выполнения.

Readonly, похоже, имеет аналогичную цель в IL . Он сообщает другим сборкам, могут ли они писать в поле определенного типа. Однако readonly не похоже на с той же проверкой во время выполнения, которую модификаторы доступа показывают в моем примере выше. Кажется, что readonly является проверкой во время компиляции и не происходит во время выполнения. Взгляните на образец производительности здесь: Производительность только для чтения против const .

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

6 голосов
/ 27 мая 2009

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

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

Поскольку статический член только для чтения установлен во время статического конструктора, JIT «знает», что значение существует. Нет дополнительной памяти - только для чтения не позволяет другим методам установить это, но это проверка времени компиляции.

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

Эксперт C ++ / CLI Маркуса Хиджи имеет достаточно хорошее объяснение этого.

4 голосов
/ 23 апреля 2012

Один важный момент, который еще не упомянут ни в каких других ответах, заключается в том, что при доступе к полю «только для чтения» или при обращении к любому свойству запрос удовлетворяется с использованием копии данных. Если данные, о которых идет речь, являются типом значения с более чем 4-8 байтами данных, стоимость такого дополнительного копирования иногда может быть значительной. Обратите внимание, что, хотя при увеличении структуры с 16 байтов до 17 наблюдается большой скачок в стоимости, структуры могут быть немного больше и при этом быстрее, чем классы во многих приложениях, , если они копируются не слишком часто, Например, если предполагается, что у вас есть тип, который представляет вершины треугольника в трехмерном пространстве. Прямой реализацией будет структура, содержащая структуру с тремя float для каждой точки; вероятно, всего 36 байтов. Если точки и координаты в каждой точке являются изменяемыми открытыми полями, можно быстро и легко получить доступ к someTriangle.P1.X, не копируя никакие данные, кроме координаты Y вершины 1. С другой стороны, если P1 было свойство или поле readonly, компилятор должен будет скопировать P1 во временную структуру, а затем прочитать X из этого.

3 голосов
/ 27 мая 2009

Даже если бы только чтение имело эффект только во время компиляции, все равно было бы необходимо сохранить данные в сборке (т.е. IL). CLR - это Common Language Runtime - классы, написанные на одном языке, могут использоваться и расширяться другими языками.

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

Конечно, тот факт, что поле помечено readonly, означает, что JIT может делать другие вещи, такие как оптимизация (например, встроенное использование значения) и т. Д. Независимо от того, что вы использовали отражение для изменения значения поля создание IL, который изменяет поле initonly вне соответствующего конструктора (экземпляр или статический, в зависимости от типа поля), приведет к непроверяемой сборке.

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