Home > Blockchain >  What is the correct fast way to compare two `Uint8List`s' equality?
What is the correct fast way to compare two `Uint8List`s' equality?

Time:01-18

Given Uint8List a = ...; Uint8List b = ...; I want to compare whether their content are the same. Of course I can use listEquals in Flutter, or write down a naive loop like:

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

However, given that Uint8List is quite a special array, and it is often quite big, I guess where there is a better (faster) approach to do so?

CodePudding user response:

I think that you should be able to get some slight speed-up by comparing bytes 4 or 8 bytes at a time (which also should have alignment benefits). This should not need to copy the byte data and therefore would not have a significant memory penalty. I wrote a quick implementation to try it:

import 'dart:typed_data';
import 'dart:math';

/// 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 list1, Uint8List list2) {
  if (identical(list1, list2)) {
    return true;
  }

  if (list1.lengthInBytes != list2.lengthInBytes) {
    return false;
  }

  var numWords = list1.lengthInBytes ~/ 8;
  var reinterpretedList1 = list1.buffer.asUint64List(0, numWords);
  var reinterpretedList2 = list2.buffer.asUint64List(0, numWords);

  for (var i = 0; i < reinterpretedList1.length; i  = 1) {
    if (reinterpretedList1[i] != reinterpretedList2[i]) {
      return false;
    }
  }

  // Compare any remaining bytes.
  for (var i = reinterpretedList1.lengthInBytes;
      i < list1.lengthInBytes;
      i  = 1) {
    if (list1[i] != list2[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();
  stopwatch.start();
  var result = listEquals(data, dataCopy);
  print('Naive:     $result ${stopwatch.elapsed}');

  stopwatch.reset();
  stopwatch.start();
  result = memEquals(data, dataCopy);
  print('memEquals: $result ${stopwatch.elapsed}');
}

My empirical results from running it as a Dart console application on my 64-bit Linux machine (dart mem_equals.dart):

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

and from compiling it (dart compile exe mem_equals.dart && mem_equals.exe):

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

CodePudding user response:

Dart's listEquals does exactly what your code does (plus a few shortcut checks) so I would use that instead of your own code, as it's cleaner. One possible alternative is to convert both lists to String and do a String equality comparison. I doubt that it is faster (because it creates a new String), but your could easily benchmark that and decide!

To convert a UInt8List to a String use

String s = String.fromCharCodes(inputAsUint8List);
  •  Tags:  
  • Related