C# 双数组相等性比较的最快性能 [已关闭]

C# Fastest performance for double array equality comparison [closed]

提问人:Shiv 提问时间:7/15/2020 最后编辑:Shiv 更新时间:12/8/2020 访问量:1655

问:


想改进这个问题吗?更新问题,使其仅通过编辑这篇文章来关注一个问题。

3年前关闭。

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

这是我所指的字节数组比较解决方案。比较 .NET 中的两个字节数组

我的目标是比迭代和比较两个双数组中的元素更快。

作为参考,我的问题需要比较这些数组大约 10 万亿次。计算的这一部分大约占操作总数的 30%,因此可以节省大量成本。

目前我们运行的是 .NET 4.6 - 4.8。

C# 数组比较 相等性

评论

1赞 TheGeneral 7/15/2020
所以比什么快?我们在这里试图击败什么,或者我们应该天生地为你做这些基准?我建议,benchmark.net 并尝试一些解决方案
0赞 Shiv 7/15/2020
@TheGeneral 比循环和比较两个双精度数组的元素更快。
0赞 TheGeneral 7/15/2020
你有没有尝试过 SequenceEquals,它是如何比较的,不安全的指针比较呢?
0赞 Shiv 7/15/2020
@TheGeneral不安全的指针比较 - memcmp 采用字节数组而不是双精度数组,因此我需要以某种方式将双指针视为具有适当长度调整的字节指针。不知道如何在不使用位转换器等将双精度数组复制到字节数组的情况下做到这一点。我担心它是次优的。
2赞 Chris 7/15/2020
如果您担心某个解决方案会次优,请将其添加到您的基准测试中。如果您不打算对潜在的解决方案进行基准测试,请花一些时间来设置 BenchmarkDotNet

答:

6赞 TheGeneral 7/15/2020 #1

更新了一些基准测试(见末尾的注释):

[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 |

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