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

时间:2018-10-26 16:02:07

标签: c# c#-7.0

我已经在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;
    }
}

vs

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

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

1 个答案:

答案 0 :(得分:3)

in修饰符通过在调用方法时避免不必要的值类型副本来提高性能。请注意,值类型(即结构)实际上并不是自动不可变的(但是C#编译器通过设置“防御性副本”,然后在设置属性值时覆盖整个父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字节要贵。您应该在运行时进行概要分析,以决定是否值得进行更改。这也会使x中的Foo不可变,因为否则将修改value中的Main