C# 双数组相等比较的最快性能

C# Fastest performance for double array equality comparison

您好,我正在尝试寻找在 C# 中比较两个双精度数组的最快方法。如果合适,很乐意使用 unsafe。我在字节比较解决方案中看到的最大问题之一是我不想将双精度数组复制到字节数组来执行 pinvoke。如果有一种将双精度数组视为字节数组以传递给 pinvoke memcmp 调用的高效方法,那就太好了。

这是我指的字节数组比较方案。 Comparing two byte arrays in .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 |

警告,这不是基准测试的最佳示例,您真的应该 运行 这些基准测试自己在您的自己的数据,在你自己的环境中。如果您要获得最佳性能,则需要考虑诸如秋季阵列扫描的可能性、阵列大小等因素。