ByteData vs Uint8List performance

4,349 views
Skip to first unread message

James Philbin

unread,
Apr 10, 2019, 3:54:18 AM4/10/19
to Dart Misc
Why is the read/write access to ByteData so much slower than Uint8List? I've done some simple benchmarks and both reading and writing are 10x slower for ByteData.
Jim

William Hesse

unread,
Apr 10, 2019, 6:39:21 AM4/10/19
to Dart Misc
On Wednesday, April 10, 2019 at 9:54:18 AM UTC+2, James Philbin wrote:
Why is the read/write access to ByteData so much slower than Uint8List? I've done some simple benchmarks and both reading and writing are 10x slower for ByteData.

Constructing a ByteData object on the Dart VM runtime (dart executable) is implemented as constructing a Uint8List, and putting a ByteView wrapper around it, so a ByteData object is literally a Uint8List wrapped in another layer:
A Uint8List is, on the other hand, a native runtime object implemented in C++.

On other platforms, and with other compilers, such as dart2js or dartdevc, the difference between ByteData and Uint8List might be completely different, though.

From /runtime/lib/typed_data.patch
@patch
class ByteData implements TypedData {
@patch
@pragma("vm:entry-point")
factory ByteData(int length) {
final list = new Uint8List(length) as _TypedList;
_rangeCheck(list.lengthInBytes, 0, length);
return new _ByteDataView(list, 0, length);
}
@patch
class Uint8List {
@patch
@pragma("vm:exact-result-type", _Uint8List)
factory Uint8List(int length) native "TypedData_Uint8Array_new";
}

Vyacheslav Egorov

unread,
Apr 10, 2019, 11:49:20 AM4/10/19
to General Dart Discussion
Hi James,

Could you share your benchmarks - then we could give you an explanation based on the code. 

It is highly dependent on how you access ByteData (e.g. whether you use host endianness or not).

Also please specify whether you run your benchmarks on Dart VM (and in which platform and in which mode, e.g. if you use it inside Flutter) or in the browser. 

// Vyacheslav Egorov


On Wed, Apr 10, 2019 at 9:54 AM James Philbin <jfph...@gmail.com> wrote:
Why is the read/write access to ByteData so much slower than Uint8List? I've done some simple benchmarks and both reading and writing are 10x slower for ByteData.
Jim

--
For more ways to connect visit https://www.dartlang.org/community
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.
To view this discussion on the web visit https://groups.google.com/a/dartlang.org/d/msgid/misc/fb205fa8-12d6-4ec9-9b2b-fb120b05287a%40dartlang.org.

James Philbin

unread,
Apr 11, 2019, 7:40:18 AM4/11/19
to Dart Misc
My benchmark compares reading bytes from Uint8List and from ByteData. It only uses the getUint8 method for ByteData. So, endianness is not specified.
I run the benchmark in the VM using pub run. Here is the code:

~~~~
import 'dart:typed_data';

void main(List<String> args) {
  const repetitions = 1024 * 64;

  const length = 512;
  final timer = Stopwatch()..start();

  int start;
  int end;
  int v;

  // Uint8List
  final uint8List = Uint8List(length);
  start = timer.elapsedMicroseconds;
  for (var j = 0; j < repetitions; j++) {
    for (var k = 0; k < length; k++) v = uint8List[k];
  }
  end = timer.elapsedMicroseconds;
  final time0 = end - start;


  // ByteData
  final bd = ByteData(length);
  start = timer.elapsedMicroseconds;
  for (var j = 0; j < repetitions; j++) {
    for (var k = 0; k < length; k++) v = bd.getUint8(k);
  }
  end = timer.elapsedMicroseconds;
  final time1 = end - start;

  print('v: $v');
  assert(v == 0);

  print('read uint8 $time0 bd $time1 ratio ${time1 / time0}');
}
~~~~
Thanks for any help you can give.
Jim

Vyacheslav Egorov

unread,
Apr 16, 2019, 11:21:35 AM4/16/19
to General Dart Discussion
Majority of the difference comes from the way your microbenchmark is written. Try changing it in a way where main is invoked multiple times, for example:

import 'dart:typed_data';

void main2(List<String> args) {
  const repetitions = 1024 * 64;
  
  // your original main goes here

  print('read uint8 $time0 bd $time1 ratio ${time1 / time0}');
}

void main(args) {
  main2(args);
  main2(args);
  main2(args);
}

This would yield something like this:

$ dart x.dart
v: 0
read uint8 18426 bd 195513 ratio 10.610713122761316
v: 0
read uint8 17528 bd 54616 ratio 3.11592879963487
v: 0
read uint8 17749 bd 48282 ratio 2.720265930474956

The rest of the difference comes from slightly different code being generated for ByteData. 

There is few more loads and comparisons - which is extremely visible on such a tight microbenchmark. If you change your code to something more real-world (e.g. length is not a known constant), like so

import 'dart:typed_data';

void main2(List<String> args, uint8List, bd) {
  const repetitions = 1024 * 64;

  final timer = Stopwatch()..start();

  int start;
  int end;
  int v;

  // Uint8List
  //final uint8List = Uint8List(length);
  start = timer.elapsedMicroseconds;
  for (var j = 0; j < repetitions; j++) {
    for (var k = 0; k < uint8List.length; k++) v = uint8List[k];
  }
  end = timer.elapsedMicroseconds;
  final time0 = end - start;


  // ByteData
  //final bd = ByteData(length);
  start = timer.elapsedMicroseconds;
  for (var j = 0; j < repetitions; j++) {
    for (var k = 0; k < bd.length; k++) v = bd.getUint8(k);
  }
  end = timer.elapsedMicroseconds;
  final time1 = end - start;

  print('v: $v');
  assert(v == 0);

  print('read uint8 $time0 bd $time1 ratio ${time1 / time0}');
}

void main(args) {
  main2(args, Uint8List(512), ByteData(512));
  main2(args, Uint8List(512), ByteData(512));
  main2(args, Uint8List(512), ByteData(512));
}

Then you get 

$ dart y.dart
v: 0
read uint8 17052 bd 300384 ratio 17.61576354679803
v: 0
read uint8 19800 bd 26496 ratio 1.3381818181818181
v: 0
read uint8 17801 bd 26637 ratio 1.4963766080557273

Which I think is quite reasonable.

// Vyacheslav Egorov


--
For more ways to connect visit https://www.dartlang.org/community
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.
Reply all
Reply to author
Forward
0 new messages