Зачем когда-либо использовать модификатор параметра "in" в C#?

Итак, я (кажется, я) понимаю, что in модификатор параметра делает. Но то, что он делает, кажется излишним.

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

Есть ли какое-то преимущество в производительности? Я был убежден, что на внутренней стороне вещей, ref Параметр должен, по крайней мере, копировать физический адрес переменной, который должен быть того же размера, что и любая типичная ссылка на объект.

Итак, есть ли преимущество только в более крупных структурах или есть какая-то закулисная оптимизация компилятора, которая делает его привлекательным в других местах? Если последнее, почему я не должен сделать каждый параметр in?

5 ответов

in Недавно был представлен язык C#.

in на самом деле ref readonly, Вообще говоря, существует только один случай использования in может быть полезным: высокопроизводительные приложения, работающие с большим количеством больших readonly structs.

Предполагая, что у вас есть:

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.

Другие вопросы по тегам