嗨,我正在努力寻找比较C#中两个双数组的最快方法。如果合适的话,很乐意使用不安全的产品。我在字节比较解决方案中看到的最大问题之一是,例如,我不想将双数组复制到字节数组中进行pinvoke。如果有一种高性能的方法可以将双数组作为字节数组来传递给pinvoke-memcmp调用,那就太好了。
这就是我所指的字节数组比较解决方案。比较.NET 中的两个字节数组
我的目标是比迭代和比较两个双数组中的元素更快。
作为参考,我的问题需要对这些数组进行大约10万亿次的比较。计算的这一部分大约占操作总数的30%,因此在这里可以节省大量费用。
目前我们运行的是.NET 4.6-4.8。
更新了一些基准测试(请参阅末尾的注释(:
[SimpleJob(RuntimeMoniker.Net472)]
public class Test
{
private double[] data1;
private double[] data2;
[Params(1000, 10000000)] public int N;
[GlobalSetup]
public void Setup()
{
var r = new Random(42);
data1 = Enumerable.Range(0, N).Select(x => r.NextDouble()).ToArray();
data2 = data1.ToArray();
}
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int memcmp(IntPtr a1, IntPtr a2, uint count);
[Benchmark]
public unsafe bool Memcmp()
{
fixed (double* p1 = data1, p2 = data2)
return memcmp((IntPtr) p1, (IntPtr) p2, (uint) data1.Length * sizeof(double)) == 0;
}
[Benchmark]
public unsafe bool UnsafeCompare()
{
fixed (double* p1 = data1, p2 = data2)
for (var i = 0; i < data1.Length; i++)
if (p1[i] != p2[i])
return false;
return true;
}
[Benchmark]
public bool SequenceEqual()
{
return data1.SequenceEqual(data2);
}
[Benchmark]
public bool RegularCompare()
{
for (var i = 0; i < data1.Length; i++)
if (data1[i] != data2[i])
return false;
return true;
}
[Benchmark]
public unsafe bool SpanSequenceEqual1()
{
fixed (double* p1 = data1, p2 = data2)
return new Span<double>(p1, data1.Length).SequenceEqual(data2);
}
[Benchmark]
public unsafe bool SpanSequenceEqual2()
{
fixed (double* p1 = data1, p2 = data2)
return new Span<double>(p1, data1.Length).SequenceEqual(new Span<double>(p2, data2.Length));
}
}
这里没有容错,谨慎地补充一下。这也是完全未经测试的。
2种阵列大小的基准:
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18362.900 (1903/May2019Update/19H1)
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
[Host] : .NET Framework 4.8 (4.8.4180.0), X64 RyuJIT
.NET 4.7.2 : .NET Framework 4.8 (4.8.4180.0), X64 RyuJIT
Job=.NET 4.7.2 Runtime=.NET 4.7.2
| Method | N | Mean | Error | StdDev |
|------------------- |--------- |-----------------:|----------------:|--------------:|
| Memcmp | 1000 | 280.0 ns | 1.59 ns | 1.49 ns |
| UnsafeCompare | 1000 | 536.5 ns | 2.35 ns | 1.84 ns |
| SequenceEqual | 1000 | 12,020.5 ns | 238.08 ns | 370.66 ns |
| RegularCompare | 1000 | 807.9 ns | 4.57 ns | 4.05 ns |
| SpanSequenceEqual1 | 1000 | 561.7 ns | 7.67 ns | 7.18 ns |
| SpanSequenceEqual2 | 1000 | 556.8 ns | 4.59 ns | 4.07 ns |
| Memcmp | 10000000 | 10,809,916.0 ns | 215,874.22 ns | 302,625.51 ns |
| UnsafeCompare | 10000000 | 11,357,531.6 ns | 226,782.10 ns | 242,654.31 ns |
| SequenceEqual | 10000000 | 117,777,038.7 ns | 1,055,512.03 ns | 987,326.61 ns |
| RegularCompare | 10000000 | 11,857,691.7 ns | 186,827.02 ns | 165,617.28 ns |
| SpanSequenceEqual1 | 10000000 | 11,371,142.7 ns | 151,452.88 ns | 141,669.12 ns |
| SpanSequenceEqual2 | 10000000 | 11,160,517.2 ns | 172,947.95 ns | 153,313.85 ns |
警告,这不是最好的基准测试示例,您应该在自己的数据和环境中自己运行这些基准测试。如果你想要达到峰值性能,你需要考虑秋季阵列扫描的可能性、阵列的大小等因素。