【问题标题】:What is the correct fast way to compare two `Uint8List`s' equality?比较两个“Uint8List”相等性的正确快速方法是什么?
【发布时间】:2022-01-18 02:25:29
【问题描述】:

鉴于Uint8List a = ...; Uint8List b = ...;我想比较他们的内容是否相同。当然我可以在 Flutter 中使用listEquals,或者写一个简单的循环,比如:

for (int index = 0; index < a.length; index += 1) {
    if (a[index] != b[index])
      return false;
  }

但是,鉴于Uint8List 是一个非常特殊的数组,而且它通常很大,我猜哪里有更好(更快)的方法来做到这一点?

【问题讨论】:

    标签: android ios flutter dart


    【解决方案1】:

    我认为您应该能够通过一次比较 4 或 8 个字节来稍微加快速度(这也应该有对齐的好处)。这应该不需要复制字节数据,因此不会有显着的内存损失。我写了一个快速实现来试试:

    import 'dart:typed_data';
    import 'dart:math' show Random;
    
    /// Naive [List] equality implementation.
    bool listEquals<E>(List<E> list1, List<E> list2) {
      if (identical(list1, list2)) {
        return true;
      }
    
      if (list1.length != list2.length) {
        return false;
      }
    
      for (var i = 0; i < list1.length; i += 1) {
        if (list1[i] != list2[i]) {
          return false;
        }
      }
    
      return true;
    }
    
    /// Compares two [Uint8List]s by comparing 8 bytes at a time.
    bool memEquals(Uint8List bytes1, Uint8List bytes2) {
      if (identical(bytes1, bytes2)) {
        return true;
      }
    
      if (bytes1.lengthInBytes != bytes2.lengthInBytes) {
        return false;
      }
    
      // Treat the original byte lists as lists of 8-byte words.
      var numWords = bytes1.lengthInBytes ~/ 8;
      var words1 = bytes1.buffer.asUint64List(0, numWords);
      var words2 = bytes2.buffer.asUint64List(0, numWords);
    
      for (var i = 0; i < words1.length; i += 1) {
        if (words1[i] != words2[i]) {
          return false;
        }
      }
    
      // Compare any remaining bytes.
      for (var i = words1.lengthInBytes; i < bytes1.lengthInBytes; i += 1) {
        if (bytes1[i] != bytes2[i]) {
          return false;
        }
      }
    
      return true;
    }
    
    void main() {
      var random = Random();
    
      // Generate random data.
      //
      // 100 MB minus a few bytes to avoid being an exact multiple of 8 bytes.
      const numBytes = 100 * 1000 * 1000 - 3;
      var data = Uint8List.fromList([
        for (var i = 0; i < numBytes; i += 1) random.nextInt(256),
      ]);
    
      var dataCopy = Uint8List.fromList(data);
    
      var stopwatch = Stopwatch()..start();
      var result = listEquals(data, dataCopy);
      print('Naive:     $result ${stopwatch.elapsed}');
    
      stopwatch
        ..reset()
        ..start();
      result = memEquals(data, dataCopy);
      print('memEquals: $result ${stopwatch.elapsed}');
    

    我在 64 位 Linux 机器 (dart mem_equals.dart) 上将其作为 Dart 控制台应用程序运行的经验结果:

    Naive:     true 0:00:00.152984
    memEquals: true 0:00:00.038664
    

    并从编译它(dart compile exe mem_equals.dart &amp;&amp; mem_equals.exe):

    Naive:     true 0:00:00.093478
    memEquals: true 0:00:00.033560
    

    我没有与使用 dart:ffi 进行比较,但作为基线,在同一系统上对相同大小的字节数组 (clang -O3 memcmp_test.c &amp;&amp; a.out) 调用 memcmp 的纯 C 程序大约需要 0.011 秒。

    【讨论】:

    • 确实我想知道 C++ 实现而不是 dart 是否会更快,因为 C++ 循环的开销应该很小。但是您的解决方案似乎已经相当不错了
    【解决方案2】:

    Dart 的 listEquals 与您的代码完全一样(加上一些快捷方式检查),所以我会使用它而不是您自己的代码,因为它更简洁。一种可能的替代方法是将两个列表都转换为String 并进行String 相等比较。我怀疑它是否更快(因为它会创建一个新的String),但您可以轻松地对其进行基准测试并做出决定!

    要将UInt8List 转换为String,请使用

    String s = String.fromCharCodes(inputAsUint8List);
    

    【讨论】:

      猜你喜欢
      • 2011-05-08
      • 1970-01-01
      • 2018-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-31
      相关资源
      最近更新 更多