"in" 具有原始值类型的修饰符?

"in" modifier with primitive value types?

我在 MSDN 上阅读了 article。它解释了为什么 "in" 应该只与自定义只读结构一起使用,否则会有性能损失。但是,我不太明白如何将 "in" 与原始类型一起使用。由于 C# 中的所有内置值类型都是不可变的,这是否意味着与按值传递相比,使用 "in" 修饰符按引用传递它们会略微提高性能?

示例:

public class Product
{
    private readonly int _weight;
    private readonly decimal _price;

    public Product(in decimal price, in int weight)
    {
        _price = price;
        _weight = weight;
    }
}

public class Product
{
    private readonly int _weight;
    private readonly decimal _price;

    public Product(decimal price, int weight)
    {
        _price = price;
        _weight = weight;
    }
}

in 修饰符通过在调用方法时避免不必要的值类型副本来提高性能。请注意,值类型(即结构)实际上并不是自动不可变的(但 C# 编译器确实通过 "defensive copies" 并在设置 [=48= 时覆盖整个父级 struct 来提供不可变的外观] 值,这在您链接到的文章中有解释。

鉴于如果您不使用 in(或 out/ref),结构将在方法调用中被完整复制,您可以通过仅传递一个指向调用堆栈中更高层结构对象的指针,因为指针(.NET 中的引用)小于结构,但这只是在结构真正不可变时避免复制。

C# 的内置值类型(Int16Int32DoubleUInt64 等)都小于(或大小相同) 64 位系统上的指针(Decimal 是 128 位类型,String 是引用类型),这意味着使用 in 的好处为零这些类型的修饰符。您还将因产生指针取消引用的成本而遭受性能损失 - 这也可能导致处理器 memory cache miss.

考虑以下一些不同的场景(所有示例都假定 x64,并且没有更改方法调用语义或调用约定的优化):

传递小值类型

public static void Main()
{
    Int32 value = 123; // 4 bytes
    Foo( in value ); // Get an 8-byte pointer to `value`, then pass that
}

public static void Foo( in Int32 x ) { ... }

性能受到影响,因为现在计算机正在传递一个也需要取消引用的 8 字节指针值,而不是可以立即使用的 4 字节值。

传递大值类型

public struct MyBigStruct
{
    public Decimal Foo;
    public Decimal Bar;
    public Decimal Baz;
}

public static void Main()
{
    MyBigStruct value; // 48 bytes
    Foo( in value ); // Get an 8-byte pointer to `value`, then pass that
}

public static void Foo( in MyBigStruct x ) { ... }

可能会提高性能,因为计算机正在传递一个 8 字节的指针值而不是复制一个 48 字节的值,但是指针取消引用 可能 比复制额外的 32 个字节。您应该在运行时分析以确定更改是否值得。这也使得 Foo 中的 x 不可变,否则 Main 中的 value 将被修改。