Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
61ac2856
Unverified
Commit
61ac2856
authored
Feb 11, 2022
by
Michael Goderbauer
Committed by
GitHub
Feb 11, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[performance] Trace direct calls to inflateWidget (#98277)
parent
d486aa70
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
136 additions
and
48 deletions
+136
-48
common.dart
dev/tracing_tests/test/common.dart
+21
-0
inflate_widget_tracing_test.dart
dev/tracing_tests/test/inflate_widget_tracing_test.dart
+85
-0
timeline_test.dart
dev/tracing_tests/test/timeline_test.dart
+7
-22
framework.dart
packages/flutter/lib/src/widgets/framework.dart
+23
-26
No files found.
dev/tracing_tests/test/common.dart
View file @
61ac2856
...
...
@@ -4,7 +4,9 @@
import
'dart:developer'
as
developer
;
import
'dart:isolate'
as
isolate
;
import
'dart:ui'
;
import
'package:flutter/scheduler.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:vm_service/vm_service.dart'
;
import
'package:vm_service/vm_service_io.dart'
;
...
...
@@ -32,3 +34,22 @@ Future<List<TimelineEvent>> fetchTimelineEvents() async {
await
_vmService
.
clearVMTimeline
();
return
timeline
.
traceEvents
!;
}
Future
<
List
<
TimelineEvent
>>
fetchInterestingEvents
(
Set
<
String
>
interestingLabels
)
async
{
return
(
await
fetchTimelineEvents
()).
where
((
TimelineEvent
event
)
{
return
interestingLabels
.
contains
(
event
.
json
![
'name'
])
&&
event
.
json
![
'ph'
]
==
'B'
;
// "Begin" mark of events, vs E which is for the "End" mark of events.
}).
toList
();
}
String
eventToName
(
TimelineEvent
event
)
=>
event
.
json
![
'name'
]
as
String
;
Future
<
List
<
String
>>
fetchInterestingEventNames
(
Set
<
String
>
interestingLabels
)
async
{
return
(
await
fetchInterestingEvents
(
interestingLabels
)).
map
<
String
>(
eventToName
).
toList
();
}
Future
<
void
>
runFrame
(
VoidCallback
callback
)
{
final
Future
<
void
>
result
=
SchedulerBinding
.
instance
.
endOfFrame
;
// schedules a frame
callback
();
return
result
;
}
dev/tracing_tests/test/inflate_widget_tracing_test.dart
0 → 100644
View file @
61ac2856
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/scheduler.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'common.dart'
;
final
Set
<
String
>
interestingLabels
=
<
String
>{
'
$Row
'
,
'
$TestRoot
'
,
'
$TestChildWidget
'
,
'
$Container
'
,
};
void
main
(
)
{
WidgetsFlutterBinding
.
ensureInitialized
();
initTimelineTests
();
test
(
'Children of MultiChildRenderObjectElement show up in tracing'
,
()
async
{
// We don't have expectations around the first frame because there's a race around
// the warm-up frame that we don't want to get involved in here.
await
runFrame
(()
{
runApp
(
const
TestRoot
());
});
await
SchedulerBinding
.
instance
.
endOfFrame
;
await
fetchInterestingEvents
(
interestingLabels
);
debugProfileBuildsEnabled
=
true
;
await
runFrame
(()
{
TestRoot
.
state
.
showRow
();
});
expect
(
await
fetchInterestingEventNames
(
interestingLabels
),
<
String
>[
'TestRoot'
,
'Row'
,
'TestChildWidget'
,
'Container'
,
'TestChildWidget'
,
'Container'
],
);
debugProfileBuildsEnabled
=
false
;
},
skip:
isBrowser
);
// [intended] uses dart:isolate and io.
}
class
TestRoot
extends
StatefulWidget
{
const
TestRoot
({
Key
?
key
})
:
super
(
key:
key
);
static
late
TestRootState
state
;
@override
State
<
TestRoot
>
createState
()
=>
TestRootState
();
}
class
TestRootState
extends
State
<
TestRoot
>
{
@override
void
initState
()
{
super
.
initState
();
TestRoot
.
state
=
this
;
}
bool
_showRow
=
false
;
void
showRow
()
{
setState
(()
{
_showRow
=
true
;
});
}
@override
Widget
build
(
BuildContext
context
)
{
return
_showRow
?
Row
(
children:
const
<
Widget
>[
TestChildWidget
(),
TestChildWidget
(),
],
)
:
Container
();
}
}
class
TestChildWidget
extends
StatelessWidget
{
const
TestChildWidget
({
Key
?
key
})
:
super
(
key:
key
);
@override
Widget
build
(
BuildContext
context
)
{
return
Container
();
}
}
dev/tracing_tests/test/timeline_test.dart
View file @
61ac2856
...
...
@@ -21,15 +21,6 @@ final Set<String> interestingLabels = <String>{
'
$RenderCustomPaint
'
,
};
Future
<
List
<
TimelineEvent
>>
fetchInterestingEvents
()
async
{
return
(
await
fetchTimelineEvents
()).
where
((
TimelineEvent
event
)
{
return
interestingLabels
.
contains
(
event
.
json
![
'name'
])
&&
event
.
json
![
'ph'
]
==
'B'
;
// "Begin" mark of events, vs E which is for the "End" mark of events.
}).
toList
();
}
String
eventToName
(
TimelineEvent
event
)
=>
event
.
json
![
'name'
]
as
String
;
class
TestRoot
extends
StatefulWidget
{
const
TestRoot
({
Key
?
key
})
:
super
(
key:
key
);
...
...
@@ -66,12 +57,6 @@ class TestRootState extends State<TestRoot> {
}
}
Future
<
void
>
runFrame
(
VoidCallback
callback
)
{
final
Future
<
void
>
result
=
SchedulerBinding
.
instance
.
endOfFrame
;
// schedules a frame
callback
();
return
result
;
}
void
main
(
)
{
WidgetsFlutterBinding
.
ensureInitialized
();
initTimelineTests
();
...
...
@@ -80,14 +65,14 @@ void main() {
// the warm-up frame that we don't want to get involved in here.
await
runFrame
(()
{
runApp
(
const
TestRoot
());
});
await
SchedulerBinding
.
instance
.
endOfFrame
;
await
fetchInterestingEvents
();
await
fetchInterestingEvents
(
interestingLabels
);
// The next few cases build the exact same tree so should have no effect.
debugProfileBuildsEnabled
=
true
;
await
runFrame
(()
{
TestRoot
.
state
.
rebuild
();
});
expect
(
(
await
fetchInterestingEvents
()).
map
<
String
>(
eventToName
),
await
fetchInterestingEventNames
(
interestingLabels
),
<
String
>[
'BUILD'
,
'LAYOUT'
,
'UPDATING COMPOSITING BITS'
,
'PAINT'
,
'COMPOSITING'
,
'FINALIZE TREE'
],
);
debugProfileBuildsEnabled
=
false
;
...
...
@@ -95,7 +80,7 @@ void main() {
debugProfileLayoutsEnabled
=
true
;
await
runFrame
(()
{
TestRoot
.
state
.
rebuild
();
});
expect
(
(
await
fetchInterestingEvents
()).
map
<
String
>(
eventToName
),
await
fetchInterestingEventNames
(
interestingLabels
),
<
String
>[
'BUILD'
,
'LAYOUT'
,
'UPDATING COMPOSITING BITS'
,
'PAINT'
,
'COMPOSITING'
,
'FINALIZE TREE'
],
);
debugProfileLayoutsEnabled
=
false
;
...
...
@@ -103,7 +88,7 @@ void main() {
debugProfilePaintsEnabled
=
true
;
await
runFrame
(()
{
TestRoot
.
state
.
rebuild
();
});
expect
(
(
await
fetchInterestingEvents
()).
map
<
String
>(
eventToName
),
await
fetchInterestingEventNames
(
interestingLabels
),
<
String
>[
'BUILD'
,
'LAYOUT'
,
'UPDATING COMPOSITING BITS'
,
'PAINT'
,
'COMPOSITING'
,
'FINALIZE TREE'
],
);
debugProfilePaintsEnabled
=
false
;
...
...
@@ -116,7 +101,7 @@ void main() {
debugProfileBuildsEnabled
=
true
;
await
runFrame
(()
{
TestRoot
.
state
.
updateWidget
(
Placeholder
(
key:
UniqueKey
(),
color:
const
Color
(
0xFFFFFFFF
)));
});
events
=
await
fetchInterestingEvents
();
events
=
await
fetchInterestingEvents
(
interestingLabels
);
expect
(
events
.
map
<
String
>(
eventToName
),
<
String
>[
'BUILD'
,
'Placeholder'
,
'CustomPaint'
,
'LAYOUT'
,
'UPDATING COMPOSITING BITS'
,
'PAINT'
,
'COMPOSITING'
,
'FINALIZE TREE'
],
...
...
@@ -127,7 +112,7 @@ void main() {
debugProfileLayoutsEnabled
=
true
;
await
runFrame
(()
{
TestRoot
.
state
.
updateWidget
(
Placeholder
(
key:
UniqueKey
()));
});
events
=
await
fetchInterestingEvents
();
events
=
await
fetchInterestingEvents
(
interestingLabels
);
expect
(
events
.
map
<
String
>(
eventToName
),
<
String
>[
'BUILD'
,
'LAYOUT'
,
'RenderCustomPaint'
,
'UPDATING COMPOSITING BITS'
,
'PAINT'
,
'COMPOSITING'
,
'FINALIZE TREE'
],
...
...
@@ -140,7 +125,7 @@ void main() {
debugProfilePaintsEnabled
=
true
;
await
runFrame
(()
{
TestRoot
.
state
.
updateWidget
(
Placeholder
(
key:
UniqueKey
()));
});
events
=
await
fetchInterestingEvents
();
events
=
await
fetchInterestingEvents
(
interestingLabels
);
expect
(
events
.
map
<
String
>(
eventToName
),
<
String
>[
'BUILD'
,
'LAYOUT'
,
'UPDATING COMPOSITING BITS'
,
'PAINT'
,
'RenderCustomPaint'
,
'COMPOSITING'
,
'FINALIZE TREE'
],
...
...
packages/flutter/lib/src/widgets/framework.dart
View file @
61ac2856
...
...
@@ -3550,36 +3550,16 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
}
else
{
deactivateChild
(
child
);
assert
(
child
.
_parent
==
null
);
if
(!
kReleaseMode
&&
debugProfileBuildsEnabled
)
{
Map
<
String
,
String
>
debugTimelineArguments
=
timelineArgumentsIndicatingLandmarkEvent
;
assert
(()
{
debugTimelineArguments
=
newWidget
.
toDiagnosticsNode
().
toTimelineArguments
();
return
true
;
}());
Timeline
.
startSync
(
'
${newWidget.runtimeType}
'
,
arguments:
debugTimelineArguments
,
);
}
// The [debugProfileBuildsEnabled] code for this branch is inside
// [inflateWidget], since some [Element]s call [inflateWidget] directly
// instead of going through [updateChild].
newChild
=
inflateWidget
(
newWidget
,
newSlot
);
if
(!
kReleaseMode
&&
debugProfileBuildsEnabled
)
Timeline
.
finishSync
();
}
}
else
{
if
(!
kReleaseMode
&&
debugProfileBuildsEnabled
)
{
Map
<
String
,
String
>
debugTimelineArguments
=
timelineArgumentsIndicatingLandmarkEvent
;
assert
(()
{
debugTimelineArguments
=
newWidget
.
toDiagnosticsNode
().
toTimelineArguments
();
return
true
;
}());
Timeline
.
startSync
(
'
${newWidget.runtimeType}
'
,
arguments:
debugTimelineArguments
,
);
}
// The [debugProfileBuildsEnabled] code for this branch is inside
// [inflateWidget], since some [Element]s call [inflateWidget] directly
// instead of going through [updateChild].
newChild
=
inflateWidget
(
newWidget
,
newSlot
);
if
(!
kReleaseMode
&&
debugProfileBuildsEnabled
)
Timeline
.
finishSync
();
}
assert
(()
{
...
...
@@ -3807,6 +3787,19 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
@pragma
(
'vm:prefer-inline'
)
Element
inflateWidget
(
Widget
newWidget
,
Object
?
newSlot
)
{
assert
(
newWidget
!=
null
);
if
(!
kReleaseMode
&&
debugProfileBuildsEnabled
)
{
Map
<
String
,
String
>
debugTimelineArguments
=
timelineArgumentsIndicatingLandmarkEvent
;
assert
(()
{
debugTimelineArguments
=
newWidget
.
toDiagnosticsNode
().
toTimelineArguments
();
return
true
;
}());
Timeline
.
startSync
(
'
${newWidget.runtimeType}
'
,
arguments:
debugTimelineArguments
,
);
}
final
Key
?
key
=
newWidget
.
key
;
if
(
key
is
GlobalKey
)
{
final
Element
?
newChild
=
_retakeInactiveElement
(
key
,
newWidget
);
...
...
@@ -3829,6 +3822,10 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
}());
newChild
.
mount
(
this
,
newSlot
);
assert
(
newChild
.
_lifecycleState
==
_ElementLifecycle
.
active
);
if
(!
kReleaseMode
&&
debugProfileBuildsEnabled
)
Timeline
.
finishSync
();
return
newChild
;
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment