From 42e3a7a8fd4550bc31e36a7456fa2eecc35aee16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Mon, 28 Apr 2025 02:33:12 -0700 Subject: [PATCH] [dart2wasm] Add more JS interop benchmarks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In prep for [1], benchmark `dartify` performance when converting JS call return values to Dart values. [1]: https://dart-review.googlesource.com/c/sdk/+/424021 Change-Id: I43549276f0ccdfefcc689c0574f82cad4a856aa5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/424940 Commit-Queue: Ömer Ağacan Reviewed-by: Martin Kustermann --- benchmarks/WasmJSInterop/WasmJSInterop.dart | 254 ++++++++++++++++++++ 1 file changed, 254 insertions(+) diff --git a/benchmarks/WasmJSInterop/WasmJSInterop.dart b/benchmarks/WasmJSInterop/WasmJSInterop.dart index 33d50cfb50b..205a91145d1 100644 --- a/benchmarks/WasmJSInterop/WasmJSInterop.dart +++ b/benchmarks/WasmJSInterop/WasmJSInterop.dart @@ -65,10 +65,213 @@ class DoublePassingBenchmark { calls; } +@JS() +external bool dartifyBool(); + +@JS() +external bool? dartifyNullableBool(); + +@JS() +external num dartifyNum(); + +@JS() +external num? dartifyNullableNum(); + +@JS() +external double dartifyDouble(); + +@JS() +external double? dartifyNullableDouble(); + +@JS() +external int dartifyInt(); + +@JS() +external int? dartifyNullableInt(); + +@JS() +external String dartifyString(); + +@JS() +external String? dartifyNullableString(); + +@JS() +external JSArray dartifyJSArray(); + +@JS() +external JSArray? dartifyNullableJSArray(); + +// `dart:typed_data` types are all boxed the same way, so no need to test each +// one of them separately. +@JS() +external JSUint8Array dartifyJSUint8Array(); + +@JS() +external JSUint8Array? dartifyNullableJSUint8Array(); + +const int ITERATIONS = 10000; + +bool boolSink = false; + +double dartifyBoolBenchmark() => + BenchmarkBase.measureFor(() { + for (int i = 0; i < ITERATIONS; i += 1) { + boolSink = dartifyBool(); + } + }, minimumMeasureDurationMillis) / + ITERATIONS; + +bool? nullableBoolSink = null; + +double dartifyNullableBoolBenchmark() => + BenchmarkBase.measureFor(() { + for (int i = 0; i < ITERATIONS; i += 1) { + nullableBoolSink = dartifyNullableBool(); + } + }, minimumMeasureDurationMillis) / + ITERATIONS; + +num numSink = 1; + +double dartifyNumBenchmark() => + BenchmarkBase.measureFor(() { + for (int i = 0; i < ITERATIONS; i += 1) { + numSink = dartifyNum(); + } + }, minimumMeasureDurationMillis) / + ITERATIONS; + +num? nullableNumSink = null; + +double dartifyNullableNumBenchmark() => + BenchmarkBase.measureFor(() { + for (int i = 0; i < ITERATIONS; i += 1) { + nullableNumSink = dartifyNullableNum(); + } + }, minimumMeasureDurationMillis) / + ITERATIONS; + +double doubleSink = 0.0; + +double dartifyDoubleBenchmark() => + BenchmarkBase.measureFor(() { + for (int i = 0; i < ITERATIONS; i += 1) { + doubleSink = dartifyDouble(); + } + }, minimumMeasureDurationMillis) / + ITERATIONS; + +double? nullableDoubleSink = null; + +double dartifyNullableDoubleBenchmark() => + BenchmarkBase.measureFor(() { + for (int i = 0; i < ITERATIONS; i += 1) { + nullableDoubleSink = dartifyNullableDouble(); + } + }, minimumMeasureDurationMillis) / + ITERATIONS; + +int intSink = 0; + +double dartifyIntBenchmark() => + BenchmarkBase.measureFor(() { + for (int i = 0; i < ITERATIONS; i += 1) { + intSink = dartifyInt(); + } + }, minimumMeasureDurationMillis) / + ITERATIONS; + +int? nullableIntSink = null; + +double dartifyNullableIntBenchmark() => + BenchmarkBase.measureFor(() { + for (int i = 0; i < ITERATIONS; i += 1) { + nullableIntSink = dartifyNullableInt(); + } + }, minimumMeasureDurationMillis) / + ITERATIONS; + +String stringSink = ""; + +double dartifyStringBenchmark() => + BenchmarkBase.measureFor(() { + for (int i = 0; i < ITERATIONS; i += 1) { + stringSink = dartifyString(); + } + }, minimumMeasureDurationMillis) / + ITERATIONS; + +String? nullableStringSink = null; + +double dartifyNullableStringBenchmark() => + BenchmarkBase.measureFor(() { + for (int i = 0; i < ITERATIONS; i += 1) { + nullableStringSink = dartifyNullableString(); + } + }, minimumMeasureDurationMillis) / + ITERATIONS; + +JSArray jsArraySink = JSArray(); + +double dartifyJSArrayBenchmark() => + BenchmarkBase.measureFor(() { + for (int i = 0; i < ITERATIONS; i += 1) { + jsArraySink = dartifyJSArray(); + } + }, minimumMeasureDurationMillis) / + ITERATIONS; + +JSArray? nullableJsArraySink = null; + +double dartifyNullableJSArrayBenchmark() => + BenchmarkBase.measureFor(() { + for (int i = 0; i < ITERATIONS; i += 1) { + nullableJsArraySink = dartifyNullableJSArray(); + } + }, minimumMeasureDurationMillis) / + ITERATIONS; + +JSUint8Array jsUint8ArraySink = JSUint8Array(); + +double dartifyJSUint8ArrayBenchmark() => + BenchmarkBase.measureFor(() { + for (int i = 0; i < ITERATIONS; i += 1) { + jsUint8ArraySink = dartifyJSUint8Array(); + } + }, minimumMeasureDurationMillis) / + ITERATIONS; + +JSUint8Array? nullableJsUint8ArraySink = null; + +double dartifyNullableJSUint8ArrayBenchmark() => + BenchmarkBase.measureFor(() { + for (int i = 0; i < ITERATIONS; i += 1) { + nullableJsUint8ArraySink = dartifyNullableJSUint8Array(); + } + }, minimumMeasureDurationMillis) / + ITERATIONS; + void main() { eval(''' self.intFun = (i) => i; self.doubleFun = (d) => d; + self.dartifyBool = () => true; + self.dartifyNullableBool = () => false; + self.dartifyNum = () => 12.34; + self.dartifyNullableNum = () => 56.78; + self.dartifyDouble = () => 10.20; + self.dartifyNullableDouble = () => 30.40; + self.dartifyInt = () => 123; + self.dartifyNullableInt = () => 456; + self.dartifyString = () => "abc"; + self.dartifyNullableString = () => "def"; + + // Arrays are all empty to not add element conversion overheads to the + // benchmark results. + self.dartifyJSArray = () => new Array(); + self.dartifyNullableJSArray = () => new Array(); + self.dartifyJSUint8Array = () => new Uint8Array(0); + self.dartifyNullableJSUint8Array = () => new Uint8Array(0); '''); final maxI31 = (1 << 30) - 1; @@ -84,6 +287,57 @@ void main() { DoublePassingBenchmark(1.0, 1.0, 10).measure(); final double = DoublePassingBenchmark(1.0, 12.34, 1000000).measure(); report('WasmJSInterop.call.void.1ArgsDouble', double); + + report('WasmJSInterop.call.bool.0Args', dartifyBoolBenchmark()); + report( + 'WasmJSInterop.call.nullableBool.0Args', + dartifyNullableBoolBenchmark(), + ); + report('WasmJSInterop.call.num.0Args', dartifyNumBenchmark()); + report('WasmJSInterop.call.nullableNum.0Args', dartifyNullableNumBenchmark()); + report('WasmJSInterop.call.double.0Args', dartifyDoubleBenchmark()); + report( + 'WasmJSInterop.call.nullableDouble.0Args', + dartifyNullableDoubleBenchmark(), + ); + report('WasmJSInterop.call.int.0Args', dartifyIntBenchmark()); + report('WasmJSInterop.call.nullableInt.0Args', dartifyNullableIntBenchmark()); + report('WasmJSInterop.call.string.0Args', dartifyStringBenchmark()); + report( + 'WasmJSInterop.call.nullableString.0Args', + dartifyNullableStringBenchmark(), + ); + report('WasmJSInterop.call.JSArray.0Args', dartifyJSArrayBenchmark()); + report( + 'WasmJSInterop.call.nullableJSArray.0Args', + dartifyNullableJSArrayBenchmark(), + ); + report( + 'WasmJSInterop.call.JSUint8Array.0Args', + dartifyJSUint8ArrayBenchmark(), + ); + report( + 'WasmJSInterop.call.nullableJSUint8Array.0Args', + dartifyNullableJSUint8ArrayBenchmark(), + ); + + // To keep the sinks alive + if (int.parse('1') == 0) { + print(boolSink); + print(nullableBoolSink); + print(numSink); + print(nullableNumSink); + print(doubleSink); + print(nullableDoubleSink); + print(intSink); + print(nullableIntSink); + print(stringSink); + print(nullableStringSink); + print(jsArraySink); + print(nullableJsArraySink); + print(jsUint8ArraySink); + print(nullableJsUint8ArraySink); + } } /// Reports in Golem-specific format.