- 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/
SQL FMDB 的修饰符在哪里描述?特别是,如何在要插入的字符串中放置引号。比如我想将名称“Mac's Place”插入数据库?谢谢 最佳答案 如果在 SQL 中使用 executeUpdate 和
我读过 article在 MSDN 上。它解释了为什么“in”应该只与自定义只读结构一起使用,否则会有性能损失。但是,我不太明白如何对原始类型使用“in”。由于 C# 中的所有内置值类型都是不可变的,
“__printflike__ 修饰符”到底是什么?这个词是什么意思? 最佳答案 据推测,它会告诉编译器您正在使用的函数接受格式为 [anything, ] format, ... 的参数,其中 fo
我在 VS2010 上使用 ReSharper 插件,我正在生成一个接口(interface)方法。ReSharper 在参数名称上放置一个 @。那是做什么用的? int Count(Func @wh
我在写小偷中用了大量的正则,所以在接下来的版本中要逐步减少正则的应用 在PHP正则表达式中需要转义的字符如下: $^*()+={}[]|/:<>.?'" 注意:perl风
http://msdn.microsoft.com/en-us/library/435f1dw2.aspx public class Base { public string Field; }
根据scala-wartremover静态分析工具我必须将“final”放在我创建的每个案例类前面:错误消息显示“案例类必须是最终的”。 根据scapegoat (Scala 的另一个静态分析工具)相
在 tcsh 中,我可以通过以下方式从路径末尾提取第二个路径元素 cd /some/long/directory/structure/path/ set x=`pwd` echo ${x:h:h:t}
我找到了很多 SwiftUI 教程,其中修饰符 .resizable() 用于图像。它似乎不再适用于带有 Swift 5 的 Xcode 13.3.1。 导入了 SwiftUI。如果您自己键入整个修饰
我已经设置好了音频播放器 除了步进器的当前功能外,我还想为 onIncrement 和 onDecrement 播放单独的声音。 这个项目使用核心数据来持久化。 $estimatorData.qty
来自 linux 内核脚本/mod/modpost.c: static int is_vmlinux(const char *modname) { const char *myname;
我刚刚遇到了一个BNF Grammar for JAVA 。其中,“修饰符”有一个称为“threadsafe”的终结符号。但是,我以前从未见过它,也无法在《Java 语言规范》Java SE 7 版
我创建了一个脚本,它将我组中的所有帖子嵌入到我的网站上。为此,我必须知道所有可能的修饰符“附件”类型。 现在我知道,那个类型可以是: 分享, video_share_youtube。 但我不知道在哪里
在 tcsh 中,我可以通过以下方式从路径末尾提取第二个路径元素 cd /some/long/directory/structure/path/ set x=`pwd` echo ${x:h:h:t}
我找到了很多 SwiftUI 教程,其中修饰符 .resizable() 用于图像。它似乎不再适用于带有 Swift 5 的 Xcode 13.3.1。 导入了 SwiftUI。如果您自己键入整个修饰
我正在编写一个代表一些简单几何形状的 Java 类。 最上面abstract类(它本身是包私有(private))我已经声明了需要从同一包中的子类访问的属性。 如果我将属性声明为 final在 Abs
我已经设置好了音频播放器 除了步进器的当前功能外,我还想为 onIncrement 和 onDecrement 播放单独的声音。 这个项目使用核心数据来持久化。 $estimatorData.qty
明确地说,我是 不是 询问如何使用 ViewModifier 协议(protocol)来创建一个带有 body 函数的结构,然后可以使用它来修改 View 。这个问题有点不同。 我正在尝试创建 Nav
我有一个内部类,用于存储我用于游戏的控件的信息,现在我想在其中存储一个静态 ArrayList,其中包含所有控件的名称。但我收到此错误:“修饰符 static 只允许在常量变量声明中” private
public class This_testing { int x,y; public This_testing(int x,int y){ //Why modifier void
我是一名优秀的程序员,十分优秀!