Skip to content

Commit 6b3d175

Browse files
alexmarkovCommit Bot
authored and
Commit Bot
committed
[vm] More efficient 'await' of not Future and completed _Future
When awaiting a value which is not a Future or a completed built-in _Future, 'await' implementation can bypass heavyweight _Future/_FutureListener machinery and schedule micro-tasks directly. Benchmarks: JIT, x64: AsyncLiveVars.* +46-54% (bigger is better) Calls.AwaitAsyncCall -46% (less is better) Calls.AwaitAsyncCallInstanceTargetPolymorphic -46% Calls.AwaitAsyncCallClosureTargetPolymorphic -45% Calls.AwaitFutureOrCallInstanceTargetPolymorphicManyAwaits -45% Calls.AwaitFutureOrCall -60% Calls.AwaitFutureOrCallInstanceTargetPolymorphic -60% Calls.AwaitFutureOrCallClosureTargetPolymorphic -59% JIT, ia32: AsyncLiveVars.* +43-52% (bigger is better) Calls.AwaitAsyncCall -42% (less is better) Calls.AwaitAsyncCallInstanceTargetPolymorphic -42% Calls.AwaitAsyncCallClosureTargetPolymorphic -41% Calls.AwaitFutureOrCallInstanceTargetPolymorphicManyAwaits -39% Calls.AwaitFutureOrCall -55% Calls.AwaitFutureOrCallInstanceTargetPolymorphic -54% Calls.AwaitFutureOrCallClosureTargetPolymorphic -53% JIT, arm: AsyncLiveVars.* +64-71% (bigger is better) Calls.AwaitAsyncCallInstanceTargetPolymorphic -51% (less is better) Calls.AwaitAsyncCallClosureTargetPolymorphic -47% Calls.AwaitFutureOrCallInstanceTargetPolymorphicManyAwaits -48% Calls.AwaitFutureOrCall -64% Calls.AwaitFutureOrCallInstanceTargetPolymorphic -64% Calls.AwaitFutureOrCallClosureTargetPolymorphic -59% JIT, arm64: AsyncLiveVars.* +65-78% (bigger is better) Calls.AwaitAsyncCall -51% (less is better) Calls.AwaitAsyncCallInstanceTargetPolymorphic -51% Calls.AwaitAsyncCallClosureTargetPolymorphic -50% Calls.AwaitFutureOrCallInstanceTargetPolymorphicManyAwaits -49% Calls.AwaitFutureOrCall -69% Calls.AwaitFutureOrCallInstanceTargetPolymorphic -68% Calls.AwaitFutureOrCallClosureTargetPolymorphic -67% AOT, x64: AsyncLiveVars.* +55-61% (bigger is better) Calls.AwaitAsyncCall -47% (less is better) Calls.AwaitAsyncCallInstanceTargetPolymorphic -46% Calls.AwaitAsyncCallClosureTargetPolymorphic -47% Calls.AwaitFutureOrCallInstanceTargetPolymorphicManyAwaits -46% Calls.AwaitFutureOrCall -59% Calls.AwaitFutureOrCallInstanceTargetPolymorphic -59% Calls.AwaitFutureOrCallClosureTargetPolymorphic -58% AOT, arm: AsyncLiveVars.* 54-66% (bigger is better) Calls.AwaitAsyncCall -46-51% (less is better) Calls.AwaitAsyncCallInstanceTargetPolymorphic -46-50% Calls.AwaitAsyncCallClosureTargetPolymorphic -46-52% Calls.AwaitFutureOrCallInstanceTargetPolymorphicManyAwaits -45-50% Calls.AwaitFutureOrCall -63-68% Calls.AwaitFutureOrCallInstanceTargetPolymorphic -63-66% Calls.AwaitFutureOrCallClosureTargetPolymorphic -63-67% AOT, arm64: AsyncLiveVars.* +53-66% (bigger is better) Calls.AwaitAsyncCall -50-51% (less is better) Calls.AwaitAsyncCallInstanceTargetPolymorphic -50% Calls.AwaitAsyncCallClosureTargetPolymorphic -50-51% Calls.AwaitFutureOrCallInstanceTargetPolymorphicManyAwaits -49-50% Calls.AwaitFutureOrCall -66-68% Calls.AwaitFutureOrCallInstanceTargetPolymorphic -66-68% Calls.AwaitFutureOrCallClosureTargetPolymorphic -63-67% TEST=ci Issue: #48378 Change-Id: I65e3702fcd816ee3fee876ff442b9887c035b1ec Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243102 Reviewed-by: Lasse Nielsen <[email protected]> Commit-Queue: Alexander Markov <[email protected]>
1 parent 213ae54 commit 6b3d175

File tree

4 files changed

+60
-10
lines changed

4 files changed

+60
-10
lines changed

pkg/vm_service/test/get_stack_test.dart

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ final tests = <IsolateTest>[
9595
(VmService service, IsolateRef isolateRef) async {
9696
final result = await service.getStack(isolateRef.id!);
9797

98-
expect(result.frames, hasLength(10));
98+
expect(result.frames, hasLength(6));
9999
expect(result.asyncCausalFrames, hasLength(26));
100100
expect(result.awaiterFrames, hasLength(13));
101101

@@ -105,10 +105,6 @@ final tests = <IsolateTest>[
105105
[equals('Regular'), anything], // Internal mech. ..
106106
[equals('Regular'), anything],
107107
[equals('Regular'), anything],
108-
[equals('Regular'), anything],
109-
[equals('Regular'), anything],
110-
[equals('Regular'), anything],
111-
[equals('Regular'), anything],
112108
[equals('Regular'), endsWith(' _RawReceivePortImpl._handleMessage')],
113109
]);
114110

runtime/observatory/tests/service/get_stack_limit_rpc_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ var tests = <IsolateTest>[
5151
var frames = stack['frames'];
5252
var asyncFrames = stack['asyncCausalFrames'];
5353
var awaiterFrames = stack['awaiterFrames'];
54-
expect(frames.length, greaterThanOrEqualTo(20));
54+
expect(frames.length, greaterThanOrEqualTo(12));
5555
expect(asyncFrames.length, greaterThan(frames.length));
5656
expect(awaiterFrames.length, greaterThan(frames.length));
5757
expect(stack['truncated'], false);

runtime/observatory_2/tests/service_2/get_stack_limit_rpc_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ var tests = <IsolateTest>[
5151
var frames = stack['frames'];
5252
var asyncFrames = stack['asyncCausalFrames'];
5353
var awaiterFrames = stack['awaiterFrames'];
54-
expect(frames.length, greaterThanOrEqualTo(20));
54+
expect(frames.length, greaterThanOrEqualTo(12));
5555
expect(asyncFrames.length, greaterThan(frames.length));
5656
expect(awaiterFrames.length, greaterThan(frames.length));
5757
expect(stack['truncated'], false);

sdk/lib/_internal/vm/lib/async_patch.dart

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,15 +372,69 @@ class _SuspendState {
372372
}
373373
}
374374

375+
@pragma("vm:invisible")
376+
@pragma("vm:prefer-inline")
377+
void _awaitCompletedFuture(_Future future) {
378+
assert(future._isComplete);
379+
final zone = Zone._current;
380+
if (future._hasError) {
381+
@pragma("vm:invisible")
382+
void run() {
383+
final AsyncError asyncError =
384+
unsafeCast<AsyncError>(future._resultOrListeners);
385+
zone.runBinary(
386+
unsafeCast<dynamic Function(Object, StackTrace)>(_errorCallback),
387+
asyncError.error,
388+
asyncError.stackTrace);
389+
}
390+
391+
zone.scheduleMicrotask(run);
392+
} else {
393+
@pragma("vm:invisible")
394+
void run() {
395+
zone.runUnary(unsafeCast<dynamic Function(dynamic)>(_thenCallback),
396+
future._resultOrListeners);
397+
}
398+
399+
zone.scheduleMicrotask(run);
400+
}
401+
}
402+
403+
@pragma("vm:invisible")
404+
@pragma("vm:prefer-inline")
405+
void _awaitNotFuture(Object? object) {
406+
final zone = Zone._current;
407+
@pragma("vm:invisible")
408+
void run() {
409+
zone.runUnary(
410+
unsafeCast<dynamic Function(dynamic)>(_thenCallback), object);
411+
}
412+
413+
zone.scheduleMicrotask(run);
414+
}
415+
375416
@pragma("vm:entry-point", "call")
376417
@pragma("vm:invisible")
377418
Object? _await(Object? object) {
378-
if (_trace) print('_awaitAsync (object=$object)');
419+
if (_trace) print('_await (object=$object)');
379420
if (_thenCallback == null) {
380421
_createAsyncCallbacks();
381422
}
382-
_awaitHelper(object, unsafeCast<dynamic Function(dynamic)>(_thenCallback),
383-
unsafeCast<dynamic Function(Object, StackTrace)>(_errorCallback));
423+
if (object is _Future) {
424+
if (object._isComplete) {
425+
_awaitCompletedFuture(object);
426+
} else {
427+
object._thenAwait<dynamic>(
428+
unsafeCast<dynamic Function(dynamic)>(_thenCallback),
429+
unsafeCast<dynamic Function(Object, StackTrace)>(_errorCallback));
430+
}
431+
} else if (object is! Future) {
432+
_awaitNotFuture(object);
433+
} else {
434+
object.then(unsafeCast<dynamic Function(dynamic)>(_thenCallback),
435+
onError:
436+
unsafeCast<dynamic Function(Object, StackTrace)>(_errorCallback));
437+
}
384438
return _functionData;
385439
}
386440

0 commit comments

Comments
 (0)