C#内存。Span异常慢
在试验新的C#内存。Span异常慢,c#,C#,在试验新的Span和Memory功能时,我发现与其他与字节数组交互的方法相比,使用Memory解析二进制数据要慢得多 我建立了一个基准测试套件,使用多种方法从数组中读取单个整数,发现内存是最慢的。正如预期的那样,它比Span慢,但令人惊讶的是,它也比直接使用阵列慢,以及我自己开发的版本,我希望内存在内部与之类似 //比较从数组读取偏移量int的各种方法的测试套件 公共类二进制测试 { 静态字节[]arr=新字节[]{0,1,2,3,4}; 静态内存mem=arr.AsMemory(); 静态Ho
Span
和Memory
功能时,我发现与其他与字节数组交互的方法相比,使用Memory
解析二进制数据要慢得多
我建立了一个基准测试套件,使用多种方法从数组中读取单个整数,发现内存是最慢的。正如预期的那样,它比Span慢,但令人惊讶的是,它也比直接使用阵列慢,以及我自己开发的版本,我希望内存在内部与之类似
//比较从数组读取偏移量int的各种方法的测试套件
公共类二进制测试
{
静态字节[]arr=新字节[]{0,1,2,3,4};
静态内存mem=arr.AsMemory();
静态HomeGrowthMemy memTest=新的HomeGrowthMemy(arr);
[基准]
公共int StraightArrayBitConverter()
{
将位转换器返回到32(arr,1);
}
[基准]
公共int MemorySlice()
{
返回BinaryPrimitives.ReadInt32LittleEndian(mem.Slice(1.Span));
}
[基准]
public int MemorySliceToSize()
{
返回BinaryPrimitives.ReadInt32LittleEndian(mem.Slice(1,4.Span));
}
[基准]
公共int MemorySpanSlice()
{
返回BinaryPrimitives.ReadInt32LittleEndian(mem.Span.Slice(1));
}
[基准]
public int MemorySpanSliceToSize()
{
返回BinaryPrimitives.ReadInt32LittleEndian(mem.Span.Slice(1,4));
}
[基准]
公共int HomegrownMemorySlice()
{
返回BinaryPrimitives.ReadInt32LittleEndian(memTest.Slice(1.Span));
}
[基准]
public int HomegrownMemorySliceToSize()
{
返回BinaryPrimitives.ReadInt32LittleEndian(memTest.Slice(1,4.Span));
}
[基准]
公共int HomegrownMemorySpanSlice()
{
返回BinaryPrimitives.ReadInt32LittleEndian(memTest.Span.Slice(1));
}
[基准]
public int HomegrownMemorySpanSliceToSize()
{
返回BinaryPrimitives.ReadInt32LittleEndian(memTest.Span.Slice(1,4));
}
[基准]
公共int SpanSlice()
{
返回BinaryPrimitives.ReadInt32LittleEndian(arr.AsSpan().Slice(1));
}
[基准]
public int slicetosize()
{
返回BinaryPrimitives.ReadInt32LittleEndian(arr.AsSpan().Slice(1,4));
}
}
//个人“实现”内存,用于测试
结构HomegrownMemory
{
字节[]_arr;
国际startPos;
整数长度;
公共家庭成长内存(字节[]b)
{
这个。_arr=b;
这是._startPos=0;
这个。_长度=b.长度;
}
公共跨距Span=>\u arr.AsSpan(开始:\u startPos,长度:\u length);
公共HomegrownMemory切片(int start)
{
返回新的HomegrownMemory()
{
_arr=_arr,
_startPos=_startPos+开始,
_长度=_长度-开始
};
}
公共HomegrownMemory切片(整数开始,整数长度)
{
返回新的HomegrownMemory()
{
_arr=_arr,
_startPos=_startPos+开始,
_长度=长度
};
}
}
以下是上述代码的BenchmarkNet结果:
BenchmarkDotNet=v0.11.5, OS=Windows 10.0.17134.765 (1803/April2018Update/Redstone4)
Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores
Frequency=3984652 Hz, Resolution=250.9629 ns, Timer=TSC
.NET Core SDK=2.1.700-preview-009618
[Host] : .NET Core 2.1.11 (CoreCLR 4.6.27617.04, CoreFX 4.6.27617.02), 64bit RyuJIT
DefaultJob : .NET Core 2.1.11 (CoreCLR 4.6.27617.04, CoreFX 4.6.27617.02), 64bit RyuJIT
| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|------------------------------- |----------:|----------:|----------:|------:|------:|------:|----------:|
| StraightArrayBitConverter | 1.0832 ns | 0.0323 ns | 0.0270 ns | - | - | - | - |
| MemorySlice | 5.8882 ns | 0.0654 ns | 0.0612 ns | - | - | - | - |
| MemorySliceToSize | 6.0191 ns | 0.0983 ns | 0.0919 ns | - | - | - | - |
| MemorySpanSlice | 5.0230 ns | 0.0626 ns | 0.0555 ns | - | - | - | - |
| MemorySpanSliceToSize | 5.0189 ns | 0.0335 ns | 0.0313 ns | - | - | - | - |
| HomegrownMemorySlice | 3.9217 ns | 0.0419 ns | 0.0392 ns | - | - | - | - |
| HomegrownMemorySliceToSize | 1.5233 ns | 0.0199 ns | 0.0186 ns | - | - | - | - |
| HomegrownMemorySpanSlice | 0.8301 ns | 0.0243 ns | 0.0227 ns | - | - | - | - |
| HomegrownMemorySpanSliceToSize | 0.8303 ns | 0.0223 ns | 0.0208 ns | - | - | - | - |
| SpanSlice | 0.6891 ns | 0.0241 ns | 0.0214 ns | - | - | - | - |
| SpanSliceToSize | 0.6804 ns | 0.0174 ns | 0.0163 ns | - | - | - | - |
所有这些计时对我来说都很有意义,除了内存
计时,它们都比我预期的慢
我的理解是,Memory
只是Span
的一个实现,它可以存在于堆上,例如。。不是ref结构
<>我本来希望它比跨度慢,但至少比平直数组实现快一点。我用国产版本获得的结果是我在内存中所期望的结果
关于内存的用例
,或者它试图实现的目标,我在这里缺少了一些基本的东西吗?看到这些结果后,我的理解似乎有些偏差
编辑:
在Cowen的评论之后,我找到了内存源代码并进行了查看。在检索span时,它似乎做了很多工作,特别是检查并强制转换它的泛型对象字段,以找出它是什么类型,以便正确地强制转换
我很惊讶他们没有提供不同的内存选项和/或提供内存工厂来构造具有更强类型的内部数据字段的类。相反,他们选择了一个字段,这个字段是一个必须不断检查/浇铸以获得跨度的对象,我觉得在使用过程中应该/将会不断发生这种情况
我仍然很好奇为什么他们会这样设计内存,更重要的是,用例是什么,它是这样设计的。我觉得很多使用Span/Memory的人都在追求速度优势,而通用对象字段似乎鼓励不要使用它。您可能需要重新设计基准,以确定罪魁祸首。第2到第5种方法都调用Memory.Span。如果您查看该方法的源代码,它相当复杂。我怀疑这种方法占用了大部分时间。谢谢你的反馈。8) 我按照你的建议看了源代码后进行了编辑。那么有人知道内存的用途吗?