为何C#的in关键字会导致性能下降?
为什么C#中
in关键字传递结构体的性能比ref和值传递更差? 我在测试提升C#性能的方法时,尝试用ref和in关键字以引用方式传递结构体,结果发现ref的性能优于普通值传递,但in的性能反而比前两者都差,测试代码和结果如下:
测试代码
using System; using System.Diagnostics; class Program { static void Main() { const int Iterations = 10000000; Point3D point = new Point3D(3, 4, 5); Stopwatch sw = new Stopwatch(); // 测试值传递 sw.Start(); for (int i = 0; i < Iterations; i++) { ComputeLengthByValue(point); } sw.Stop(); Console.WriteLine($"Elapsed time without 'in': {sw.ElapsedMilliseconds} ms"); // 测试in关键字传递 sw.Reset(); sw.Start(); for (int i = 0; i < Iterations; i++) { ComputeLengthByIn(in point); } sw.Stop(); Console.WriteLine($"Elapsed time with 'in': {sw.ElapsedMilliseconds} ms"); // 测试ref关键字传递 sw.Reset(); sw.Start(); for (int i = 0; i < Iterations; i++) { ComputeLengthByRef(ref point); } sw.Stop(); Console.WriteLine($"Elapsed time with 'ref': {sw.ElapsedMilliseconds} ms"); } public static double ComputeLengthByValue(Point3D point) { return point.ComputeLength(); } public static double ComputeLengthByIn(in Point3D point) { return point.ComputeLength(); } public static double ComputeLengthByRef(ref Point3D point) { return point.ComputeLength(); } } public struct Point3D { public double X; public double Y; public double Z; public Point3D(double x, double y, double z) { X = x; Y = y; Z = z; } public double ComputeLength() { return Math.Sqrt(X * X + Y * Y + Z * Z); } }
测试结果
| 参数关键字 | 耗时 |
|---|---|
| 无in或ref(值传递) | 11 ms |
| 使用in | 14 ms |
| 使用ref | 10 ms |
原因分析
1. 防御性副本的额外开销
in参数是只读引用,编译器会确保它不会被修改。但你的Point3D.ComputeLength()方法没有标记为readonly,编译器默认会假设这个实例方法可能修改结构体成员。为了保证in参数的只读性,编译器会在调用ComputeLength()时生成一个防御性副本——相当于把in引用的结构体复制一份再调用方法,额外增加了复制开销,导致性能下降。
而ref是可写引用,编译器不需要做这种防护,直接通过引用访问结构体成员;值传递本身就是复制一次结构体,开销远小于in的“引用+防御性复制”两步操作。
2. 小型结构体的场景不适合用in
你的Point3D是仅包含三个double的小型结构体(大小24字节),值传递的复制开销本来就极小,in的引用传递优势完全体现不出来,反而因为防御性副本拖慢了性能。in的真正优势是传递大型结构体(比如几十、上百字节的结构体)时,避免值传递的大量复制操作。
解决方法
把ComputeLength()方法标记为readonly,告诉编译器这个方法不会修改结构体成员,这样就不会生成防御性副本,in的性能就能和ref持平:
public readonly double ComputeLength() { return Math.Sqrt(X * X + Y * Y + Z * Z); }
内容的提问来源于stack exchange,提问作者Captain Quack
相关产品推荐
相关产品推荐

