-
Notifications
You must be signed in to change notification settings - Fork 184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove dynamic calls #579
Remove dynamic calls #579
Conversation
I'm running an internal global presubmit, and will post back here. |
I am... super confused about that CI failure. Does not seem related... |
final valueType = fieldType & ~0x07; | ||
|
||
if ((fieldType & PbFieldType._PACKED_BIT) != 0) { | ||
if (!fieldValue.isEmpty) { | ||
fieldValue as List<Int64>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we write this code as
final listValue = fieldValue as List<Object?>;
and then use listValue
? I find such code easier to read. This applies to all other cases.
(also I doubt it's always List<Int64>
, though I am not sure).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done and switched to List<Object?>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like the new fieldValue as List<Object?>;
pattern enabled by flow analysis / type promotion :) it feels strange to convert fieldValue as List<Object?>;
to final listValue = fieldValue as List<Object?>;
, because fieldValue
is still promoted in such an assignment, so no errors pop up. The subsequent uses of fieldValue
as a List<Object?>
continue to work, and I must hunt down each reference to fieldValue
, in the scope of the cast, and change it to refer to listValue
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW using a variable also allows to satisfy dart2js constraints, you could write
final List<Object?> listValue = fieldValue;
to promote at 0 cost for dart2js.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as
checks incur a runtime cost on dart2js and are likely slower than the dynamic calls they replace. Please benchmark the changes for dart2js before committing.
One way to make the checks go away in production dart2js compiles is to use the fact that dart2js trusts implicit checks at -O3
and -O4
.
This explicit check
final messageMap = message._fieldSet._values[field.index!] as PbMap?;
can be made implicit like this:
final PbMap? messageMap = message._fieldSet._values[field.index!] as dynamic;
OK I've removed all explicit casts. I think the owners of this package should be making the tougher calls here. |
Lets sit on this PR and return to it in a bit (~1 month). I don't have time to run benchmarks right now, but in a month we will have somebody on board who can take care of this. |
Regarding With the current version of the library these are the results I'm getting in my benchmarks:
If I replace those casts to diff --git a/lib/src/json/sink/object_writer.dart b/lib/src/json/sink/object_writer.dart
index af5b562..2dab549 100644
--- a/lib/src/json/sink/object_writer.dart
+++ b/lib/src/json/sink/object_writer.dart
@@ -38,10 +38,12 @@ class JsonObjectWriter implements JsonWriter<Object?> {
} else {
var top = _stack.last;
if (_key != null) {
- (top as Map<String?, dynamic>)[_key] = value;
+ Map<String?, dynamic> map = top as dynamic;
+ map[_key] = value;
_key = null;
} else {
- (top as List<dynamic>).add(value);
+ List<dynamic> list = top as dynamic;
+ list.add(value);
}
}
} These are the results I get:
It literally halves the runtime! |
After realizing how costly type casts are when targeting JS (see my comment above), I re-implemented this PR myself in osa1@1dcf951. Results: (copied from commit message) File size:
JS runtime:
AOT runtime:
Avoiding dynamic calls and explicit type casts is a big win on JS. However on native it's slower on most benchmarks. It's unclear to me whether we want to make that trade. To me the fundamental problem is that JS and native targets are wildly different, and what is an optimization for one can easily be a pessimization in other. The only solution that I can see is conditional compilation, which is difficult to do in Dart, and too much conditional compilation also makes testing more difficult and code unmaintainable. I'm just hoping that Wasm will be mature enough soon (the spec, implementations in browsers, dart2wasm) and we will be able to target Wasm instead of JS.. |
I don't really have any horse in this race. I opened this PR for @apwilson who was trying to remove dynamic calls in order to reduce the build size of a Flutter app (not to improve runtime performance). But feel free to close if the tradeoff is not worth it. |
I was trying to understand why AOT results are worse in my comment above, and I realized that results are noisy and even when I compare master-to-master using the two work dirs that I have, one of them generate much worse results. I will try to figure out how to run these benchmarks more reliably and update the numbers. |
I can't update this PR so created #685. |
Work towards #578
Many of these fixes were guesses. Function
f
takes adynamic
which is later indexed with[]
in a sub-path of the function. Functiong
passes a value to functionf
but that value is alsodynamic
.dynamic
all the way down. The bright side is that these guesses are less guess-y than dynamic invocations. :)I did not land any fixes in tests. This PR is to improve app size.