[io] Faster readIntoSync for UInt8List.
Adds a specialized code path to File_ReadInto which avoids a memory copy when writing to a UInt8List.
Benchmarks:
https://docs.google.com/spreadsheets/d/1AqT5bDCaRfxalK9WLqGlm_EfVpTmeQT7LEqaJ-Hyl1c/edit?usp=sharing&resourcekey=0-NivVhxs0J1GM4BHXHK5gAQ
Tested: unit tests
Change-Id: I7d6cb5257da1724e8c61412b0f84bee22da298ab
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/280560
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Brian Quinlan <bquinlan@google.com>
diff --git a/runtime/bin/file.cc b/runtime/bin/file.cc
index 32fb230..b62d825 100644
--- a/runtime/bin/file.cc
+++ b/runtime/bin/file.cc
@@ -246,10 +246,37 @@
Dart_Handle result = Dart_ListLength(buffer_obj, &array_len);
ThrowIfError(result);
ASSERT(end <= array_len);
- uint8_t* buffer = Dart_ScopeAllocate(length);
+
+ uint8_t* buffer;
+ bool is_byte_data = false;
+
+ if (Dart_IsTypedData(buffer_obj)) {
+ // Avoid a memory copy if the input List<int> is an UInt8List.
+ Dart_TypedData_Type data_type;
+ intptr_t bytes_count;
+ ThrowIfError(Dart_TypedDataAcquireData(buffer_obj, &data_type,
+ reinterpret_cast<void**>(&buffer),
+ &bytes_count));
+ if (data_type == Dart_TypedData_kUint8) {
+ is_byte_data = true;
+ buffer += start;
+ ASSERT(bytes_count == array_len);
+ } else {
+ ThrowIfError(Dart_TypedDataReleaseData(buffer_obj));
+ }
+ }
+
+ if (!is_byte_data) {
+ buffer = Dart_ScopeAllocate(length);
+ }
+
int64_t bytes_read = file->Read(reinterpret_cast<void*>(buffer), length);
if (bytes_read >= 0) {
- result = Dart_ListSetAsBytes(buffer_obj, start, buffer, bytes_read);
+ if (is_byte_data) {
+ result = Dart_TypedDataReleaseData(buffer_obj);
+ } else {
+ result = Dart_ListSetAsBytes(buffer_obj, start, buffer, bytes_read);
+ }
if (Dart_IsError(result)) {
Dart_SetReturnValue(args, result);
} else {
diff --git a/tests/standalone/io/file_test.dart b/tests/standalone/io/file_test.dart
index 5491ea8..07ca4a5 100644
--- a/tests/standalone/io/file_test.dart
+++ b/tests/standalone/io/file_test.dart
@@ -825,19 +825,20 @@
asyncTestDone("testReadInto");
}
- static void testReadIntoSync() {
+ static void testReadIntoSync(
+ List<int> Function(int length, int fill) listFactory) {
File file = new File(tempDirectory.path + "/out_read_into_sync");
var openedFile = file.openSync(mode: FileMode.write);
openedFile.writeFromSync(const [1, 2, 3]);
openedFile.setPositionSync(0);
- var list = [-1, -1, -1];
+ var list = listFactory(3, 49);
Expect.equals(3, openedFile.readIntoSync(list));
Expect.listEquals([1, 2, 3], list);
read(start, end, length, expected) {
- var list = [-1, -1, -1];
+ var list = listFactory(3, 49);
openedFile.setPositionSync(0);
Expect.equals(length, openedFile.readIntoSync(list, start, end));
Expect.listEquals(expected, list);
@@ -845,11 +846,11 @@
}
read(0, 3, 3, [1, 2, 3]);
- read(0, 2, 2, [1, 2, -1]);
- read(1, 2, 1, [-1, 1, -1]);
- read(1, 3, 2, [-1, 1, 2]);
- read(2, 3, 1, [-1, -1, 1]);
- read(0, 0, 0, [-1, -1, -1]);
+ read(0, 2, 2, [1, 2, 49]);
+ read(1, 2, 1, [49, 1, 49]);
+ read(1, 3, 2, [49, 1, 2]);
+ read(2, 3, 1, [49, 49, 1]);
+ read(0, 0, 0, [49, 49, 49]);
openedFile.closeSync();
}
@@ -1764,7 +1765,13 @@
testTruncate();
testTruncateSync();
testReadInto();
- testReadIntoSync();
+ testReadIntoSync(List<int>.filled);
+ // readIntoSync has an optimized code path for UInt8List.
+ testReadIntoSync(
+ (length, fill) => Uint8List(length)..fillRange(0, length, fill));
+ // readIntoSync should worked with typed data that is not uint8.
+ testReadIntoSync(
+ (length, fill) => Uint16List(length)..fillRange(0, length, fill));
testWriteFrom();
testWriteFromSync();
testCloseException();
diff --git a/tests/standalone_2/io/file_test.dart b/tests/standalone_2/io/file_test.dart
index 3f58df2..19aee21 100644
--- a/tests/standalone_2/io/file_test.dart
+++ b/tests/standalone_2/io/file_test.dart
@@ -829,19 +829,20 @@
asyncTestDone("testReadInto");
}
- static void testReadIntoSync() {
+ static void testReadIntoSync(
+ List<int> Function(int length, int fill) listFactory) {
File file = new File(tempDirectory.path + "/out_read_into_sync");
var openedFile = file.openSync(mode: FileMode.write);
openedFile.writeFromSync(const [1, 2, 3]);
openedFile.setPositionSync(0);
- var list = <int>[null, null, null];
+ var list = listFactory(3, 49);
Expect.equals(3, openedFile.readIntoSync(list));
Expect.listEquals([1, 2, 3], list);
read(start, end, length, expected) {
- var list = <int>[null, null, null];
+ var list = listFactory(3, 49);
openedFile.setPositionSync(0);
Expect.equals(length, openedFile.readIntoSync(list, start, end));
Expect.listEquals(expected, list);
@@ -849,11 +850,11 @@
}
read(0, 3, 3, [1, 2, 3]);
- read(0, 2, 2, [1, 2, null]);
- read(1, 2, 1, [null, 1, null]);
- read(1, 3, 2, [null, 1, 2]);
- read(2, 3, 1, [null, null, 1]);
- read(0, 0, 0, [null, null, null]);
+ read(0, 2, 2, [1, 2, 49]);
+ read(1, 2, 1, [49, 1, 49]);
+ read(1, 3, 2, [49, 1, 2]);
+ read(2, 3, 1, [49, 49, 1]);
+ read(0, 0, 0, [49, 49, 49]);
openedFile.closeSync();
}
@@ -1765,7 +1766,13 @@
testTruncate();
testTruncateSync();
testReadInto();
- testReadIntoSync();
+ testReadIntoSync((length, fill) => List<int>.filled(length, fill));
+ // readIntoSync has an optimized code path for UInt8List.
+ testReadIntoSync(
+ (length, fill) => Uint8List(length)..fillRange(0, length, fill));
+ // readIntoSync should worked with typed data that is not uint8.
+ testReadIntoSync(
+ (length, fill) => Uint16List(length)..fillRange(0, length, fill));
testWriteFrom();
testWriteFromSync();
testCloseException();