"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# 的内置值类型(Int16
、Int32
、Double
、UInt64
等)都小于(或大小相同) 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
将被修改。
我在 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# 的内置值类型(Int16
、Int32
、Double
、UInt64
等)都小于(或大小相同) 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
将被修改。