Уклонение от удара по производительности при использовании `in` со структурой, не делая структуру доступной только для чтения? - PullRequest
0 голосов
/ 26 мая 2018

C # 7.2 добавлены две новые функции:

  1. In Parameters

    Используя in для параметра, давайте перейдем по ссылке, нозатем мешает нам присвоить ему значение.Однако производительность может на самом деле ухудшиться, потому что она создает «защитную копию» структуры, копируя все это

  2. Readonly Structs

    Способом обойти это является использование readonly для struct.Когда вы передаете его в параметр in, компилятор видит, что это readonly, и не будет создавать защитную копию, что делает его лучшей альтернативой для предварительного исполнения.

Это все замечательно, но каждое поле в struct должно быть readonly.Это не работает:

public readonly struct Coord
{
    public int X, Y;    // Error: instance fields of readonly structs must be read only
}

Авто-свойства также должны быть readonly.

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

1 Ответ

0 голосов
/ 26 мая 2018

Когда вы передаете [a readonly struct] во входной параметр, компилятор видит, что он доступен только для чтения и не будет создавать защитную копию.

Я думаю, вы неправильно поняли.Компилятор создает защитную копию переменной только для чтения, которая содержит struct (это может быть параметр in, но также поле readonly) при вызове метода для этого struct.

Рассмотрим следующий код :

struct S
{
    int x, y;

    public void M() {}
}

class C
{
    static void Foo()
    {
        S s = new S();
        Bar(s);
    }

    static void Bar(in S s)
    {
        s.M();
    }
}

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

Для Foo IL:

ldloca.s 0 // load address of the local s to the stack
initobj S  // initialize struct S at the address on the stack
ldloca.s 0 // load address of the local s to the stack again
call void C::Bar(valuetype S&) // call Bar
ret        // return

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

IL для Bar:

ldarg.0     // load argument s (which is an address) to the stack
ldobj S     // copy the value from the address on the stack to the stack
stloc.0     // store the value from the stack to an unnamed local variable
ldloca.s 0  // load the address of the unnamed local variable to the stack
call instance void S::M() // call M
ret         // return

Здесь инструкции ldobj и stloc создают защитную копию, чтобы убедиться, что если M мутирует struct, s не будет видоизменяться (поскольку он доступен только для чтения).

Если вы измените код так, чтобы S a readonly struct, то IL для Foo останетсято же самое, но для Bar оно изменяется на:

ldarg.0 // load argument s (which is an address) to the stack
call instance void S::M() // call M
ret     // return

Обратите внимание, что здесь больше нет копирования.

Это защитаскопируйте эту отметку struct как readonly.Но если вы не будете вызывать какие-либо методы экземпляра в структуре, защитных копий не будет.

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

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