mirror of
https://github.com/dart-lang/sdk
synced 2024-09-30 03:58:31 +00:00
[dart2wasm] Optimize String & Byte transfer across WasmGC<->JS boundary
Leads to 30-50% improvements across variety of WasmDataTransfer.* benchmarks. [0] https://dart-review.googlesource.com/c/sdk/+/372660 Change-Id: I57aa9e9679e2bdb413783591f4dc9cf9ced1966e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/372780 Reviewed-by: Ömer Ağacan <omersa@google.com>
This commit is contained in:
parent
af9067061c
commit
09a7e4f52b
|
@ -38,29 +38,31 @@ const jsRuntimeBlobPart3 = r'''
|
|||
// Converts a Dart List to a JS array. Any Dart objects will be converted, but
|
||||
// this will be cheap for JSValues.
|
||||
function arrayFromDartList(constructor, list) {
|
||||
const length = dartInstance.exports.$listLength(list);
|
||||
const array = new constructor(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
array[i] = dartInstance.exports.$listRead(list, i);
|
||||
}
|
||||
return array;
|
||||
const exports = dartInstance.exports;
|
||||
const read = exports.$listRead;
|
||||
const length = exports.$listLength(list);
|
||||
const array = new constructor(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
array[i] = read(list, i);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
buildArgsList = function(list) {
|
||||
const dartList = dartInstance.exports.$makeStringList();
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
dartInstance.exports.$listAdd(dartList, stringToDartString(list[i]));
|
||||
}
|
||||
return dartList;
|
||||
const dartList = dartInstance.exports.$makeStringList();
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
dartInstance.exports.$listAdd(dartList, stringToDartString(list[i]));
|
||||
}
|
||||
return dartList;
|
||||
}
|
||||
|
||||
// A special symbol attached to functions that wrap Dart functions.
|
||||
const jsWrappedDartFunctionSymbol = Symbol("JSWrappedDartFunction");
|
||||
|
||||
function finalizeWrapper(dartFunction, wrapped) {
|
||||
wrapped.dartFunction = dartFunction;
|
||||
wrapped[jsWrappedDartFunctionSymbol] = true;
|
||||
return wrapped;
|
||||
wrapped.dartFunction = dartFunction;
|
||||
wrapped[jsWrappedDartFunctionSymbol] = true;
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
// Imports
|
||||
|
|
|
@ -249,8 +249,7 @@ Function unwrapJSWrappedDartFunction(WasmExternRef? f) =>
|
|||
WasmExternRef? jsInt8ArrayFromDartInt8List(Int8List l) =>
|
||||
JS<WasmExternRef?>('l => arrayFromDartList(Int8Array, l)', l);
|
||||
|
||||
WasmExternRef? jsUint8ArrayFromDartUint8List(Uint8List l) =>
|
||||
JS<WasmExternRef?>('l => arrayFromDartList(Uint8Array, l)', l);
|
||||
external WasmExternRef? jsUint8ArrayFromDartUint8List(Uint8List l);
|
||||
|
||||
WasmExternRef? jsUint8ClampedArrayFromDartUint8ClampedList(
|
||||
Uint8ClampedList l) =>
|
||||
|
@ -276,9 +275,10 @@ WasmExternRef? jsFloat64ArrayFromDartFloat64List(Float64List l) =>
|
|||
|
||||
WasmExternRef? jsDataViewFromDartByteData(ByteData data, int length) =>
|
||||
JS<WasmExternRef?>("""(data, length) => {
|
||||
const read = dartInstance.exports.\$byteDataGetUint8;
|
||||
const view = new DataView(new ArrayBuffer(length));
|
||||
for (let i = 0; i < length; i++) {
|
||||
view.setUint8(i, dartInstance.exports.\$byteDataGetUint8(data, i));
|
||||
view.setUint8(i, read(data, i));
|
||||
}
|
||||
return view;
|
||||
}""", data, length.toWasmI32());
|
||||
|
|
|
@ -5,38 +5,54 @@
|
|||
import 'dart:_internal' show patch, unsafeCast;
|
||||
import 'dart:_js_helper' show JS;
|
||||
import 'dart:_string';
|
||||
import 'dart:_typed_data';
|
||||
import 'dart:_wasm';
|
||||
import 'dart:typed_data';
|
||||
|
||||
@patch
|
||||
@pragma('wasm:prefer-inline')
|
||||
JSStringImpl jsStringFromDartString(String s) {
|
||||
if (s is OneByteString) {
|
||||
return JSStringImpl(JS<WasmExternRef>(r'''
|
||||
(s, length) => {
|
||||
let result = '';
|
||||
let index = 0;
|
||||
while (index < length) {
|
||||
let chunkLength = Math.min(length - index, 0xFFFF);
|
||||
const array = new Array(chunkLength);
|
||||
for (let i = 0; i < chunkLength; i++) {
|
||||
array[i] = dartInstance.exports.$stringRead1(s, index++);
|
||||
(s, length) => {
|
||||
if (length == 0) return '';
|
||||
|
||||
const read = dartInstance.exports.$stringRead1;
|
||||
let result = '';
|
||||
let index = 0;
|
||||
const chunkLength = Math.min(length - index, 500);
|
||||
let array = new Array(chunkLength);
|
||||
while (index < length) {
|
||||
const newChunkLength = Math.min(length - index, 500);
|
||||
for (let i = 0; i < newChunkLength; i++) {
|
||||
array[i] = read(s, index++);
|
||||
}
|
||||
if (newChunkLength < chunkLength) {
|
||||
array = array.slice(0, newChunkLength);
|
||||
}
|
||||
result += String.fromCharCode(...array);
|
||||
}
|
||||
result += String.fromCharCode(...array);
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
''', jsObjectFromDartObject(s), s.length.toWasmI32()));
|
||||
''', jsObjectFromDartObject(s), s.length.toWasmI32()));
|
||||
}
|
||||
if (s is TwoByteString) {
|
||||
return JSStringImpl(JS<WasmExternRef>(r'''
|
||||
(s, length) => {
|
||||
if (length == 0) return '';
|
||||
|
||||
const read = dartInstance.exports.$stringRead2;
|
||||
let result = '';
|
||||
let index = 0;
|
||||
const chunkLength = Math.min(length - index, 500);
|
||||
let array = new Array(chunkLength);
|
||||
while (index < length) {
|
||||
let chunkLength = Math.min(length - index, 0xFFFF);
|
||||
const array = new Array(chunkLength);
|
||||
for (let i = 0; i < chunkLength; i++) {
|
||||
array[i] = dartInstance.exports.$stringRead2(s, index++);
|
||||
const newChunkLength = Math.min(length - index, 500);
|
||||
for (let i = 0; i < newChunkLength; i++) {
|
||||
array[i] = read(s, index++);
|
||||
}
|
||||
if (newChunkLength < chunkLength) {
|
||||
array = array.slice(0, newChunkLength);
|
||||
}
|
||||
result += String.fromCharCode(...array);
|
||||
}
|
||||
|
@ -50,33 +66,217 @@ JSStringImpl jsStringFromDartString(String s) {
|
|||
|
||||
@patch
|
||||
@pragma('wasm:prefer-inline')
|
||||
String jsStringToDartString(JSStringImpl s) => JS<String>(r'''
|
||||
(s, length) => {
|
||||
String jsStringToDartString(JSStringImpl s) {
|
||||
final length = s.length;
|
||||
if (length == 0) return '';
|
||||
|
||||
return JS<String>(r'''
|
||||
(s) => {
|
||||
let length = s.length;
|
||||
let range = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
range |= s.codePointAt(i);
|
||||
}
|
||||
const exports = dartInstance.exports;
|
||||
if (range < 256) {
|
||||
const dartString = dartInstance.exports.$stringAllocate1(length);
|
||||
if (length <= 10) {
|
||||
if (length == 1) {
|
||||
return exports.$stringAllocate1_1(s.codePointAt(0));
|
||||
}
|
||||
if (length == 2) {
|
||||
return exports.$stringAllocate1_2(s.codePointAt(0), s.codePointAt(1));
|
||||
}
|
||||
if (length == 3) {
|
||||
return exports.$stringAllocate1_3(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2));
|
||||
}
|
||||
if (length == 4) {
|
||||
return exports.$stringAllocate1_4(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3));
|
||||
}
|
||||
if (length == 5) {
|
||||
return exports.$stringAllocate1_5(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4));
|
||||
}
|
||||
if (length == 6) {
|
||||
return exports.$stringAllocate1_6(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5));
|
||||
}
|
||||
if (length == 7) {
|
||||
return exports.$stringAllocate1_7(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6));
|
||||
}
|
||||
if (length == 8) {
|
||||
return exports.$stringAllocate1_8(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6), s.codePointAt(7));
|
||||
}
|
||||
if (length == 9) {
|
||||
return exports.$stringAllocate1_9(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6), s.codePointAt(7), s.codePointAt(8));
|
||||
}
|
||||
if (length == 10) {
|
||||
return exports.$stringAllocate1_10(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6), s.codePointAt(7), s.codePointAt(8), s.codePointAt(9));
|
||||
}
|
||||
}
|
||||
const dartString = exports.$stringAllocate1(length);
|
||||
const write = exports.$stringWrite1;
|
||||
for (let i = 0; i < length; i++) {
|
||||
dartInstance.exports.$stringWrite1(dartString, i, s.codePointAt(i));
|
||||
write(dartString, i, s.codePointAt(i));
|
||||
}
|
||||
return dartString;
|
||||
} else {
|
||||
const dartString = dartInstance.exports.$stringAllocate2(length);
|
||||
const dartString = exports.$stringAllocate2(length);
|
||||
const write = exports.$stringWrite2;
|
||||
for (let i = 0; i < length; i++) {
|
||||
dartInstance.exports.$stringWrite2(dartString, i, s.charCodeAt(i));
|
||||
write(dartString, i, s.charCodeAt(i));
|
||||
}
|
||||
return dartString;
|
||||
}
|
||||
}
|
||||
''', s.toExternRef, s.length.toWasmI32());
|
||||
''', s.toExternRef);
|
||||
}
|
||||
|
||||
@patch
|
||||
@pragma('wasm:prefer-inline')
|
||||
WasmExternRef? jsUint8ArrayFromDartUint8List(Uint8List l) =>
|
||||
JS<WasmExternRef?>("""(data, length) => {
|
||||
const jsBytes = new Uint8Array(length);
|
||||
const getByte = dartInstance.exports.\$uint8ListGet;
|
||||
for (let i = 0; i < length; i++) {
|
||||
jsBytes[i] = getByte(data, i);
|
||||
}
|
||||
return jsBytes;
|
||||
}""", l, l.length.toWasmI32());
|
||||
|
||||
@pragma("wasm:export", "\$stringAllocate1")
|
||||
OneByteString _stringAllocate1(WasmI32 length) {
|
||||
return OneByteString.withLength(length.toIntSigned());
|
||||
}
|
||||
|
||||
@pragma("wasm:export", "\$stringAllocate1_1")
|
||||
OneByteString _stringAllocate1_1(WasmI32 a0) {
|
||||
final result = OneByteString.withLength(1);
|
||||
result.setUnchecked(0, a0.toIntSigned());
|
||||
return result;
|
||||
}
|
||||
|
||||
@pragma("wasm:export", "\$stringAllocate1_2")
|
||||
OneByteString _stringAllocate1_2(WasmI32 a0, WasmI32 a1) {
|
||||
final result = OneByteString.withLength(2);
|
||||
result.setUnchecked(1, a1.toIntSigned());
|
||||
result.setUnchecked(0, a0.toIntSigned());
|
||||
return result;
|
||||
}
|
||||
|
||||
@pragma("wasm:export", "\$stringAllocate1_3")
|
||||
OneByteString _stringAllocate1_3(WasmI32 a0, WasmI32 a1, WasmI32 a2) {
|
||||
final result = OneByteString.withLength(3);
|
||||
result.setUnchecked(2, a2.toIntSigned());
|
||||
result.setUnchecked(1, a1.toIntSigned());
|
||||
result.setUnchecked(0, a0.toIntSigned());
|
||||
return result;
|
||||
}
|
||||
|
||||
@pragma("wasm:export", "\$stringAllocate1_4")
|
||||
OneByteString _stringAllocate1_4(
|
||||
WasmI32 a0, WasmI32 a1, WasmI32 a2, WasmI32 a3) {
|
||||
final result = OneByteString.withLength(4);
|
||||
result.setUnchecked(3, a3.toIntSigned());
|
||||
result.setUnchecked(2, a2.toIntSigned());
|
||||
result.setUnchecked(1, a1.toIntSigned());
|
||||
result.setUnchecked(0, a0.toIntSigned());
|
||||
return result;
|
||||
}
|
||||
|
||||
@pragma("wasm:export", "\$stringAllocate1_5")
|
||||
OneByteString _stringAllocate1_5(
|
||||
WasmI32 a0, WasmI32 a1, WasmI32 a2, WasmI32 a3, WasmI32 a4) {
|
||||
final result = OneByteString.withLength(5);
|
||||
result.setUnchecked(4, a4.toIntSigned());
|
||||
result.setUnchecked(3, a3.toIntSigned());
|
||||
result.setUnchecked(2, a2.toIntSigned());
|
||||
result.setUnchecked(1, a1.toIntSigned());
|
||||
result.setUnchecked(0, a0.toIntSigned());
|
||||
return result;
|
||||
}
|
||||
|
||||
@pragma("wasm:export", "\$stringAllocate1_6")
|
||||
OneByteString _stringAllocate1_6(
|
||||
WasmI32 a0, WasmI32 a1, WasmI32 a2, WasmI32 a3, WasmI32 a4, WasmI32 a5) {
|
||||
final result = OneByteString.withLength(6);
|
||||
result.setUnchecked(5, a5.toIntSigned());
|
||||
result.setUnchecked(4, a4.toIntSigned());
|
||||
result.setUnchecked(3, a3.toIntSigned());
|
||||
result.setUnchecked(2, a2.toIntSigned());
|
||||
result.setUnchecked(1, a1.toIntSigned());
|
||||
result.setUnchecked(0, a0.toIntSigned());
|
||||
return result;
|
||||
}
|
||||
|
||||
@pragma("wasm:export", "\$stringAllocate1_7")
|
||||
OneByteString _stringAllocate1_7(WasmI32 a0, WasmI32 a1, WasmI32 a2, WasmI32 a3,
|
||||
WasmI32 a4, WasmI32 a5, WasmI32 a6) {
|
||||
final result = OneByteString.withLength(7);
|
||||
result.setUnchecked(6, a6.toIntSigned());
|
||||
result.setUnchecked(5, a5.toIntSigned());
|
||||
result.setUnchecked(4, a4.toIntSigned());
|
||||
result.setUnchecked(3, a3.toIntSigned());
|
||||
result.setUnchecked(2, a2.toIntSigned());
|
||||
result.setUnchecked(1, a1.toIntSigned());
|
||||
result.setUnchecked(0, a0.toIntSigned());
|
||||
return result;
|
||||
}
|
||||
|
||||
@pragma("wasm:export", "\$stringAllocate1_8")
|
||||
OneByteString _stringAllocate1_8(WasmI32 a0, WasmI32 a1, WasmI32 a2, WasmI32 a3,
|
||||
WasmI32 a4, WasmI32 a5, WasmI32 a6, WasmI32 a7) {
|
||||
final result = OneByteString.withLength(8);
|
||||
result.setUnchecked(7, a7.toIntSigned());
|
||||
result.setUnchecked(6, a6.toIntSigned());
|
||||
result.setUnchecked(5, a5.toIntSigned());
|
||||
result.setUnchecked(4, a4.toIntSigned());
|
||||
result.setUnchecked(3, a3.toIntSigned());
|
||||
result.setUnchecked(2, a2.toIntSigned());
|
||||
result.setUnchecked(1, a1.toIntSigned());
|
||||
result.setUnchecked(0, a0.toIntSigned());
|
||||
return result;
|
||||
}
|
||||
|
||||
@pragma("wasm:export", "\$stringAllocate1_9")
|
||||
OneByteString _stringAllocate1_9(WasmI32 a0, WasmI32 a1, WasmI32 a2, WasmI32 a3,
|
||||
WasmI32 a4, WasmI32 a5, WasmI32 a6, WasmI32 a7, WasmI32 a8) {
|
||||
final result = OneByteString.withLength(9);
|
||||
result.setUnchecked(8, a8.toIntSigned());
|
||||
result.setUnchecked(7, a7.toIntSigned());
|
||||
result.setUnchecked(6, a6.toIntSigned());
|
||||
result.setUnchecked(5, a5.toIntSigned());
|
||||
result.setUnchecked(4, a4.toIntSigned());
|
||||
result.setUnchecked(3, a3.toIntSigned());
|
||||
result.setUnchecked(2, a2.toIntSigned());
|
||||
result.setUnchecked(1, a1.toIntSigned());
|
||||
result.setUnchecked(0, a0.toIntSigned());
|
||||
return result;
|
||||
}
|
||||
|
||||
@pragma("wasm:export", "\$stringAllocate1_10")
|
||||
OneByteString _stringAllocate1_10(
|
||||
WasmI32 a0,
|
||||
WasmI32 a1,
|
||||
WasmI32 a2,
|
||||
WasmI32 a3,
|
||||
WasmI32 a4,
|
||||
WasmI32 a5,
|
||||
WasmI32 a6,
|
||||
WasmI32 a7,
|
||||
WasmI32 a8,
|
||||
WasmI32 a9) {
|
||||
final result = OneByteString.withLength(10);
|
||||
result.setUnchecked(9, a9.toIntSigned());
|
||||
result.setUnchecked(8, a8.toIntSigned());
|
||||
result.setUnchecked(7, a7.toIntSigned());
|
||||
result.setUnchecked(6, a6.toIntSigned());
|
||||
result.setUnchecked(5, a5.toIntSigned());
|
||||
result.setUnchecked(4, a4.toIntSigned());
|
||||
result.setUnchecked(3, a3.toIntSigned());
|
||||
result.setUnchecked(2, a2.toIntSigned());
|
||||
result.setUnchecked(1, a1.toIntSigned());
|
||||
result.setUnchecked(0, a0.toIntSigned());
|
||||
return result;
|
||||
}
|
||||
|
||||
@pragma("wasm:export", "\$stringRead1")
|
||||
WasmI32 _stringRead1(OneByteString string, WasmI32 index) {
|
||||
return string.codeUnitAtUnchecked(index.toIntSigned()).toWasmI32();
|
||||
|
@ -101,3 +301,11 @@ WasmI32 _stringRead2(TwoByteString string, WasmI32 index) {
|
|||
void _stringWrite2(TwoByteString string, WasmI32 index, WasmI32 codePoint) {
|
||||
string.setUnchecked(index.toIntSigned(), codePoint.toIntSigned());
|
||||
}
|
||||
|
||||
@pragma("wasm:export", "\$uint8ListGet")
|
||||
WasmI32 _uint8ListGet(Uint8List bytes, WasmI32 index) {
|
||||
if (bytes is U8List) {
|
||||
return bytes[index.toIntSigned()].toWasmI32();
|
||||
}
|
||||
return bytes[index.toIntSigned()].toWasmI32();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import 'dart:_internal' show patch, unsafeCast;
|
||||
import 'dart:_string' show JSStringImpl;
|
||||
import 'dart:_wasm';
|
||||
import 'dart:typed_data';
|
||||
|
||||
@patch
|
||||
@pragma('wasm:prefer-inline')
|
||||
|
@ -13,3 +14,9 @@ JSStringImpl jsStringFromDartString(String s) => unsafeCast<JSStringImpl>(s);
|
|||
@patch
|
||||
@pragma('wasm:prefer-inline')
|
||||
String jsStringToDartString(JSStringImpl s) => s;
|
||||
|
||||
@patch
|
||||
@pragma('wasm:prefer-inline')
|
||||
WasmExternRef? jsUint8ArrayFromDartUint8List(Uint8List l) =>
|
||||
throw UnsupportedError(
|
||||
'In JS compatibility mode we only support JS typed data implementations.');
|
||||
|
|
Loading…
Reference in a new issue