是什么好处在那里储存和QUOT;这&Q​​UOT;在一个结构方法的局部变量?在那里、变量、局部、好处

2023-09-03 09:54:40 作者:那樣的你

我今天浏览.NET的核心源代码树和整个这个模式的在 System.Collections.Immutable.ImmutableArray< T>

I was browsing the .NET Core source tree today and ran across this pattern in System.Collections.Immutable.ImmutableArray<T>:

T IList<T>.this[int index]
{
    get
    {
        var self = this;
        self.ThrowInvalidOperationIfNotInitialized();
        return self[index];
    }
    set { throw new NotSupportedException(); }
}

此模式(存储在一个局部变量)似乎在此文​​件中一致地应用,只要会否则引用中相同的方法多次,而不是当它仅引用一次。于是我开始思考什么比较优势可能是做这种方式;这在我看来,好处是可能的表现相关的,所以我就沿着这条路远一点......也许我可以俯瞰东西。

This pattern (storing this in a local variable) seems to be consistently applied in this file whenever this would otherwise be referenced multiple times in the same method, but not when it is only referenced once. So I started thinking about what the relative advantages might be to doing it this way; it seems to me that the advantage is likely performance-related, so I went down this route a little further... maybe I'm overlooking something else.

借助 CIL 是被发射的商店这个在当地的模式似乎看起来就像一个 ldarg.0 ,然后 ldobj UnderlyingType ,那么 stloc.0 这样以后引用来而不是从 ldloc.0 ldarg 0.0 喜欢它是只使用多次。

The CIL that gets emitted for the "store this in a local" pattern seems to look something like a ldarg.0, then ldobj UnderlyingType, then stloc.0 so that later references come from ldloc.0 instead of a bare ldarg.0 like it would be to just use this multiple times.

也许 ldarg.0 是显著慢于 ldloc.0 ,但还不足以对不管是C# - 到CIL翻译或抖动寻找机会优化这个对我们来说,这样更有意义写这个看起来怪怪的图案在C#code任何时候,我们会以其他方式发出两次 ldarg在一个结构的实例方法0.0 的说明?

Maybe ldarg.0 is significantly slower than ldloc.0, but not by enough for either the C#-to-CIL translation or the JITter to look for opportunities to optimize this for us, such that it makes more sense to write this weird-looking pattern in C# code any time we would otherwise emit two ldarg.0 instructions in a struct instance method?

更新:或者,你知道的,我可以看着这个评论在该文件的顶部,其中解释究竟发生了什么...... 的

Update: or, you know, I could have looked at the comments at the top of that file, which explain exactly what's going on...

推荐答案

正如你已经注意到了,System.Collections.Immutable.ImmutableArray&LT; T>是结构

As you already noticed, System.Collections.Immutable.ImmutableArray<T> is a struct:

public partial struct ImmutableArray<T> : ...
{
    ...

    T IList<T>.this[int index]
    {
        get
        {
            var self = this;
            self.ThrowInvalidOperationIfNotInitialized();
            return self[index];
        }
        set { throw new NotSupportedException(); }
    }

    ...

无功自我=这一点; 按这个的创建结构中提到的一个副本。为什么它应该需要做的是什么?该source这种结构的意见给的,为什么它是必要的解释:

var self = this; creates a copy of the struct referred to by this. Why should it need to do that? The source comments of this struct give an explanation of why it is necessary:

///这个类型应该是线程安全的。作为一个结构,它不能保护它   自己的领域   ///从一个线程,而其成员上执行的其他线程正在更改   ///因为结构可以的到位的简单重新分配含有领域的变化   ///此结构。因此,这是极其重要的是   /// **每个成员都应该只提领一次。 **   ///如果会员需要引用数组字段,则计为这个非关联化。   ///调用其它的实例成员(属性或方法)也算作解除引用这一点。   ///需要任何成员使用过一次,而不是一定要这更   ///指定这一个局部变量,并利用它来进行的code休息吧。   ///这有效地复制了一个在结构局部变量字段,以便   ///它是从其他线程绝缘

/// This type should be thread-safe. As a struct, it cannot protect its own fields /// from being changed from one thread while its members are executing on other threads /// because structs can change in place simply by reassigning the field containing /// this struct. Therefore it is extremely important that /// ** Every member should only dereference this ONCE. ** /// If a member needs to reference the array field, that counts as a dereference of this. /// Calling other instance members (properties or methods) also counts as dereferencing this. /// Any member that needs to use this more than once must instead /// assign this to a local variable and use that for the rest of the code instead. /// This effectively copies the one field in the struct to a local variable so that /// it is insulated from other threads.

在总之,如果可能的是正在执行的get方法的同时其他线程正在改变该结构的一个字段或改变代替结构(通过重新分配此结构类型的类成员字段,例如)从而可能造成不良的副作用,那么它对于GET方法处理之前先使该结构的(本地)副本将成为必要的。

In short, if it is possible that other threads are making changes to a field of the struct or changing the struct in place (by reassigning a class member field of this struct type, for example) while the get method is being executed and thus could cause bad side effects, then it becomes necessary for the get method to first make a (local) copy of the struct before processing it.

更新:也请阅读 supercats回答,其中详细解释了其中的条件必须得到​​满足,这样喜欢做的操作一个结构(即无功自我=这一点; )的本地副本,是为线程安全的,什么都可能发生,如果这些条件得不到满足

Update: Please also read supercats answer, which explains in detail which conditions must be fulfilled so that an operation like making a local copy of a struct (i.e. var self = this;) is being thread-safe, and what could happen if those conditions are not met.