[dart2wasm] Pass Dart double as f64 to JS interop
Don't convert Dart `double`s to `externref`s when calling JS interop
functions, pass them as `double`.
We pass other unboxed values (`int`s and `bool`s) as `externref`s, as
before:
- `int`s are most efficiently passed as `externref`s, as small integers
can be converted to `i31ref` and externalized without allocation.
- `bool`s passed as `i32` mostly work because JS treats 0 as false and
everything else as true, but the values can still be observed as
numbers rather than bools, which causes some test failures.
Changes:
- Make raw interop procedures take `double` as argument, when the Dart
type for the interop function argument is non-nullable `double`.
- Pass static type of the value and expected type (by the interop
function) to `jsifyValue`.
- `jsifyValue` then takes the static type and expected type into account
to avoid conversions when both are `double`s.
- `jsifyValue` is refactored to avoid the type conversion mapping
allocation on every call.
New benchmark result before the changes:
WasmJSInterop.call.void.1ArgsDouble(RunTimeRaw): 0.018275229357798167 ns.
After:
WasmJSInterop.call.void.1ArgsDouble(RunTimeRaw): 0.014034965034965034 ns.
Issue: https://github.com/dart-lang/sdk/issues/60357
Change-Id: Ia70671f9a8e14f359f1119beda123e94aacdd2cd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/422480
Commit-Queue: Ömer Ağacan <omersa@google.com>
Reviewed-by: Srujan Gaddam <srujzs@google.com>
This commit is contained in:
committed by
Commit Queue
parent
55dc02c2b1
commit
db448306dc
@@ -19,12 +19,15 @@ import 'package:benchmark_harness/benchmark_harness.dart';
|
||||
@JS()
|
||||
external void eval(String code);
|
||||
|
||||
// This returns `void` to avoid adding `dartify` overheads to the benchmark
|
||||
// These return `void` to avoid adding `dartify` overheads to the benchmark
|
||||
// results.
|
||||
// V8 can't figure out this doesn't do anything so the loop and JS calls aren't
|
||||
// eliminated.
|
||||
// V8 can't figure out that these don't do anything so the loops and JS calls
|
||||
// aren't eliminated.
|
||||
@JS()
|
||||
external void intId(int i);
|
||||
external void intFun(int i);
|
||||
|
||||
@JS()
|
||||
external void doubleFun(double d);
|
||||
|
||||
// Run benchmarked code for at least 2 seconds.
|
||||
const int minimumMeasureDurationMillis = 2000;
|
||||
@@ -38,15 +41,34 @@ class IntPassingBenchmark {
|
||||
double measure() =>
|
||||
BenchmarkBase.measureFor(() {
|
||||
for (int i = start; i < end; i += 1) {
|
||||
intId(i);
|
||||
intFun(i);
|
||||
}
|
||||
}, minimumMeasureDurationMillis) /
|
||||
(end - start);
|
||||
}
|
||||
|
||||
class DoublePassingBenchmark {
|
||||
final double start;
|
||||
final double step;
|
||||
final int calls;
|
||||
|
||||
DoublePassingBenchmark(this.start, this.step, this.calls);
|
||||
|
||||
double measure() =>
|
||||
BenchmarkBase.measureFor(() {
|
||||
double d = start;
|
||||
for (int i = 0; i < calls; i += 1) {
|
||||
doubleFun(d);
|
||||
d *= step;
|
||||
}
|
||||
}, minimumMeasureDurationMillis) /
|
||||
calls;
|
||||
}
|
||||
|
||||
void main() {
|
||||
eval('''
|
||||
self.intId = (i) => i;
|
||||
self.intFun = (i) => i;
|
||||
self.doubleFun = (d) => d;
|
||||
''');
|
||||
|
||||
final maxI31 = (1 << 30) - 1;
|
||||
@@ -56,6 +78,12 @@ void main() {
|
||||
|
||||
final large = IntPassingBenchmark(maxI31 + 1, maxI31 + 1000001).measure();
|
||||
report('WasmJSInterop.call.void.1ArgsInt', large);
|
||||
|
||||
// Have more than one call site to the `double` benchmark to avoid inlining
|
||||
// too much, and for fair comparison with the `int` benchmark above.
|
||||
DoublePassingBenchmark(1.0, 1.0, 10).measure();
|
||||
final double = DoublePassingBenchmark(1.0, 12.34, 1000000).measure();
|
||||
report('WasmJSInterop.call.void.1ArgsDouble', double);
|
||||
}
|
||||
|
||||
/// Reports in Golem-specific format.
|
||||
|
||||
Reference in New Issue
Block a user