作者热门文章
- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
引入了 C# 7.2 reference semantics with value-types ,除此之外,微软还开发了类似 Span<T>
and ReadOnlySpan<T>
的类型潜在地提高需要在连续内存区域上执行操作的应用程序的性能。
根据文档,一种可能提高性能的方法是通过添加 in
来通过引用传递不可变结构。这些类型的参数修饰符:
void PerformOperation(in SomeReadOnlyStruct value)
{
}
我想知道我是否应该对 ReadOnlySpan<T>
这样的类型执行此操作.我是否应该像这样声明接受只读跨度的方法:
void PerformOperation<T>(in ReadOnlySpan<T> value)
{
}
或简单地喜欢:
void PerformOperation<T>(ReadOnlySpan<T> value)
{
}
前者是否会比后者提供任何性能优势?我找不到任何在这两个方向上明确建议的文档,但我确实找到了 this example他们在那里演示了一种接受 ReadOnlySpan
的方法并且没有使用 in
修饰符。
最佳答案
Marc 的回答似乎很准确。我发布这个只是为了用一些基准来补充他自己的答案,这些基准证实了他所说的话。
我设置了以下基准类:
public class SpanBenchmarks
{
private const int Iterations = 100_000;
private byte[] _data;
private LargeStruct _control;
[GlobalSetup]
public void GlobalSetup()
{
_data = new byte[1000];
new Random().NextBytes(_data);
_control = new LargeStruct(_data[0], _data[1], _data[2], _data[3], _data[4], _data[5]);
}
[Benchmark]
public void PassSpanByValue()
{
for (int i = 0; i < Iterations; i++) AcceptSpanByValue(_data);
}
[Benchmark]
public void PassSpanByRef()
{
for (int i = 0; i < Iterations; i++) AcceptSpanByRef(_data);
}
[Benchmark]
public void PassLargeStructByValue()
{
for (int i = 0; i < Iterations; i++) AcceptLargeStructByValue(_control);
}
[Benchmark]
public void PassLargeStructByRef()
{
for (int i = 0; i < Iterations; i++) AcceptLargeStructByRef(_control);
}
private int AcceptSpanByValue(ReadOnlySpan<byte> span) => span.Length;
private int AcceptSpanByRef(in ReadOnlySpan<byte> span) => span.Length;
private decimal AcceptLargeStructByValue(LargeStruct largeStruct) => largeStruct.A;
private decimal AcceptLargeStructByRef(in LargeStruct largeStruct) => largeStruct.A;
private readonly struct LargeStruct
{
public LargeStruct(decimal a, decimal b, decimal c, decimal d, decimal e, decimal f)
{
A = a;
B = b;
C = c;
D = d;
E = e;
F = f;
}
public decimal A { get; }
public decimal B { get; }
public decimal C { get; }
public decimal D { get; }
public decimal E { get; }
public decimal F { get; }
}
}
我用这个重复了 3 次相同的基准测试,每次都得到相似的结果:
BenchmarkDotNet=v0.10.13, OS=Windows 10 Redstone 3 [1709, Fall Creators Update] (10.0.16299.248)
Intel Core i7-4790 CPU 3.60GHz (Haswell), 1 CPU, 8 logical cores and 4 physical cores
Frequency=3507500 Hz, Resolution=285.1033 ns, Timer=TSC
.NET Core SDK=2.1.300-preview2-008354
[Host] : .NET Core 2.0.6 (CoreCLR 4.6.26212.01, CoreFX 4.6.26212.01), 64bit RyuJIT
DefaultJob : .NET Core 2.0.6 (CoreCLR 4.6.26212.01, CoreFX 4.6.26212.01), 64bit RyuJIT
Method | Mean | Error | StdDev |
----------------------- |----------:|----------:|----------:|
PassSpanByValue | 641.71 us | 0.1758 us | 0.1644 us |
PassSpanByRef | 642.62 us | 0.1524 us | 0.1190 us |
PassLargeStructByValue | 390.78 us | 0.2633 us | 0.2463 us |
PassLargeStructByRef | 35.33 us | 0.3446 us | 0.3055 us |
使用大型结构作为控件,我确认通过引用而不是通过值传递它们具有显着的性能优势。但是,传递 Span<T>
之间没有显着的性能差异。通过引用或值。
2019 年 9 月更新
出于好奇,我使用 .NET Core 2.2 再次运行相同的基准测试。自上次以来似乎引入了一些巧妙的优化,以减少将数组隐式转换为 Span<T>
的开销。 :
BenchmarkDotNet=v0.11.5, OS=Windows 10.0.17134.984 (1803/April2018Update/Redstone4)
Intel Core i7-4700HQ CPU 2.40GHz (Haswell), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=2.2.106
[Host] : .NET Core 2.2.4 (CoreCLR 4.6.27521.02, CoreFX 4.6.27521.01), 64bit RyuJIT
DefaultJob : .NET Core 2.2.4 (CoreCLR 4.6.27521.02, CoreFX 4.6.27521.01), 64bit RyuJIT
| Method | Mean | Error | StdDev |
|----------------------- |----------:|----------:|----------:|
| PassSpanByValue | 39.78 us | 0.1873 us | 0.1660 us |
| PassSpanByRef | 41.21 us | 0.2618 us | 0.2186 us |
| PassLargeStructByValue | 475.41 us | 1.3104 us | 1.0943 us |
| PassLargeStructByRef | 39.75 us | 0.1001 us | 0.0937 us |
关于c# - ReadOnlySpan<T> 参数是否应使用 "in"修饰符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49297751/
我是一名优秀的程序员,十分优秀!