我试图测试添加到C#中的“in”关键字的性能(或不)。 in关键字应该能够将对值类型的只读引用传递给方法,而不是先复制值然后将其传递。
通过绕过此副本,in应该会更快,但是在我的测试中,它似乎一点都没有更快。
我正在使用BenchMarkDotNet来对我的代码进行基准测试。代码如下:
public struct Input
{
public decimal Number1 { get; set; }
public decimal Number2 { get; set; }
}
public class InBenchmarking
{
const int loops = 50000000;
Input inputInstance;
public InBenchmarking()
{
inputInstance = new Input
{
};
}
[Benchmark]
public decimal DoSomethingRefLoop()
{
decimal result = 0M;
for (int i = 0; i < loops; i++)
{
result = DoSomethingRef(ref inputInstance);
}
return result;
}
[Benchmark]
public decimal DoSomethingInLoop()
{
decimal result = 0M;
for (int i = 0; i < loops; i++)
{
result = DoSomethingIn(inputInstance);
}
return result;
}
[Benchmark(Baseline = true)]
public decimal DoSomethingLoop()
{
decimal result = 0M;
for (int i = 0; i < loops; i++)
{
result = DoSomething(inputInstance);
}
return result;
}
public decimal DoSomething(Input input)
{
return input.Number1;
}
public decimal DoSomethingIn(in Input input)
{
return input.Number1;
}
public decimal DoSomethingRef(ref Input input)
{
return input.Number1;
}
}
如您所见,我包含一个循环以使用“ref”关键字,该关键字也通过引用传递,但不是只读的。这似乎确实更快。
该测试的结果是:
Method | Mean | Error | StdDev | Scaled | ScaledSD |
------------------- |---------:|----------:|----------:|-------:|---------:|
DoSomethingRefLoop | 20.15 ms | 0.3967 ms | 0.6058 ms | 0.41 | 0.03 |
DoSomethingInLoop | 48.88 ms | 0.9756 ms | 2.5529 ms | 0.98 | 0.08 |
DoSomethingLoop | 49.84 ms | 1.0872 ms | 3.1367 ms | 1.00 | 0.00 |
因此,使用“in”似乎根本不快。我觉得有些事情可能以我无法预料的方式进行了优化,这就是性能差异的原因。我尝试将结构的大小最多增加16个十进制字段,但同样,in和按值之间没有区别。
如何构造基准测试,以真正看到in,ref和按值传递之间的区别?
最佳答案
问题是您使用的是非readonly
结构,因此编译器正在DoSomethingIn
方法中创建输入参数的防御性副本。
发生这种情况是因为您使用的是Number1
属性的getter方法,并且编译器不确定结构状态是否会因此而改变(并且由于参数作为只读引用传递,因此无效) 。
如果您像这样编辑结构:
public readonly struct Input
{
public decimal Number1 { get; }
public decimal Number2 { get; }
}
并再次运行基准测试,使用
in
方法将获得与使用ref
方法相同的性能,就像您最初的假设一样。注意:不是
readonly struct
修饰符,您也可以通过直接暴露字段来解决此问题,如下所示:public struct Input
{
public decimal Number1;
public decimal Number2;
}
重点是,如here所述:
编辑#2:以进一步阐明为什么需要
readonly struct
修饰符(同样,in
与ref readonly
相同),这是文档中的另一段:关于C#7.2关键字效果,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48142900/