Зачем когда-либо использовать модификатор параметра "in" в C#?
Итак, я (кажется, я) понимаю, что in
модификатор параметра делает. Но то, что он делает, кажется излишним.
Обычно я думаю, что единственная причина использовать ref
было бы изменить вызывающую переменную, которая явно запрещена in
, Так проходя мимо in
ссылка кажется логически эквивалентной передаче по значению.
Есть ли какое-то преимущество в производительности? Я был убежден, что на внутренней стороне вещей, ref
Параметр должен, по крайней мере, копировать физический адрес переменной, который должен быть того же размера, что и любая типичная ссылка на объект.
Итак, есть ли преимущество только в более крупных структурах или есть какая-то закулисная оптимизация компилятора, которая делает его привлекательным в других местах? Если последнее, почему я не должен сделать каждый параметр in
?
5 ответов
in
Недавно был представлен язык C#.
in
на самом деле ref readonly
, Вообще говоря, существует только один случай использования in
может быть полезным: высокопроизводительные приложения, работающие с большим количеством больших readonly struct
s.
Предполагая, что у вас есть:
readonly struct VeryLarge
{
public readonly long Value1;
public readonly long Value2;
public long Compute() { }
// etc
}
а также
void Process(in VeryLarge value) { }
В этом случае VeryLarge
структура будет передаваться по ссылке без создания защитных копий при использовании этой структуры в Process
метод (например, при вызове value.Compute()
), и неизменность структуры обеспечивается компилятором.
Обратите внимание, что прохождение не только для чтения struct
с in
модификатор заставит компилятор создавать защитную копию при вызове методов структуры и обращении к свойствам в Process
метод выше, что негативно скажется на производительности!
Есть действительно хорошая запись в блоге MSDN, которую я рекомендую внимательно прочитать.
Если вы хотите получить более исторический фон in
-внедрение, вы можете прочитать это обсуждение в репозитории GitHub языка C#.
В целом, большинство разработчиков согласны с тем, что внедрение in
можно рассматривать как ошибку. Это довольно экзотическая языковая функция, которая может быть полезна только в случаях с высокими характеристиками.
проходя мимо
in
ссылка кажется логически эквивалентной передаче по значению.
Правильный.
Есть ли какое-то преимущество в производительности?
Да.
Я был убежден, что на внутренней стороне вещей,
ref
Параметр должен, по крайней мере, копировать физический адрес переменной, который должен быть того же размера, что и любая типичная ссылка на объект.
Нет требования, чтобы ссылка на объект и ссылка на переменную имели одинаковый размер, и не существует требования, что это либо размер машинного слова, но да, на практике оба 32 бита на 32 битовые машины и 64 бит на 64 битных машинах.
Как вы думаете, при чем здесь "физический адрес", мне неясно. В Windows мы используем виртуальные адреса, а не физические адреса в коде режима пользователя. Мне интересно знать, при каких возможных обстоятельствах вы представляете себе физический адрес в программе на C#.
Также не требуется, чтобы в качестве виртуального адреса хранилища использовалась ссылка любого рода. Ссылки могут быть непрозрачными дескрипторами в таблицы GC в соответствующей реализации спецификации CLI.
преимущество только в больших структурах?
Снижение стоимости прохождения более крупных структур является мотивирующим сценарием для этой функции.
Обратите внимание, что нет никакой гарантии, что in
делает любую программу на самом деле быстрее, и это может сделать программы медленнее. На все вопросы об эффективности необходимо ответить эмпирическим исследованием. Существует очень мало оптимизаций, которые всегда выигрывают; это не оптимизация "всегда выигрывать".
есть ли какая-то закулисная оптимизация компилятора, которая делает его привлекательным в других местах?
Компилятору и среде выполнения разрешается выполнять любую оптимизацию, которую они выбирают, если это не нарушает правила спецификации C#. Насколько мне известно, еще нет такой оптимизации для in
параметров, но это не исключает такой оптимизации в будущем.
почему я не должен сделать каждый параметр в?
Ну, предположим, вы сделали int
параметр вместо in int
параметр. Какие расходы налагаются?
- сайт вызова теперь требует переменную, а не значение
- переменная не может быть зарегистрирована. Тщательно настроенная схема распределения регистров джиттера только что была добавлена в него.
- код на сайте вызова больше, потому что он должен взять ссылку на переменную и поместить ее в стек, тогда как раньше он мог просто поместить значение в стек вызова
- больший код означает, что некоторые инструкции по быстрому переходу теперь могут стать инструкциями по длинному переходу, поэтому снова код стал больше. Это влияет на все виды вещей. Кеши заполняются быстрее, у джиттера есть больше работы, джиттер может отказаться от определенных оптимизаций при больших размерах кода и так далее.
- на сайте вызываемого мы превратили доступ к значению в стеке (или регистру) в косвенное указатель на указатель. Теперь этот указатель, скорее всего, будет в кеше, но мы все же превратили доступ к значению из одной инструкции в доступ из двух команд.
- И так далее.
Предположим, это double
и вы меняете его на in double
, Опять же, теперь переменная не может быть зарегистрирована в высокопроизводительном регистре с плавающей запятой. Это не только влияет на производительность, но также может изменить поведение программы! C# разрешается выполнять арифметику с плавающей точкой с точностью выше 64 бит и обычно делает это только в том случае, если числа с плавающей точкой могут быть зарегистрированы.
Это не бесплатная оптимизация. Вы должны измерить его производительность против альтернатив. Лучше всего, во-первых, просто не создавать большие структуры, как предлагают рекомендации по дизайну.
Есть. При прохождении struct
, in
Ключевое слово позволяет оптимизировать, когда компилятору нужно только передать указатель, без риска изменения метода методом. Последнее имеет решающее значение - оно избегает операции копирования. На больших структурах это может изменить мир.
в нем только ссылка в C# 7.2
это означает, что вы не передаете весь объект в стек функций, как в случае, если вы передаете только ссылку на структуру
но попытка изменить значение объекта выдает ошибку компилятора.
И да, это позволит вам оптимизировать производительность кода, если вы используете большие структуры.
Это сделано из-за подхода функционального программирования. Одним из основных принципов является то, что функция не должна иметь побочных эффектов, что означает, что она не должна изменять значения параметров и должна возвращать некоторое значение. В C# не было способа передать структуры (и тип значения) без копирования только по ссылке, что позволяет изменять значение. В Swift есть хакерский алгоритм, который копирует структуру (их коллекции - структуры BTW), пока метод начинает изменять свои значения. Люди, которые используют Swift, не все знают о копировании. Это хорошая функция C#, так как она эффективна в памяти и явно. Если вы посмотрите на то, что нового, вы увидите, что все больше и больше делается вокруг структур и массивов в стеке. И в заявлении просто необходим для этих функций. В других ответах упоминаются ограничения, но они не так важны для понимания того, куда движется.net.