Flutter Performance Check — Part 2

Dave Parth
8 min readJan 15, 2020

--

I have received good feedback from the last part and wanted to do more. One of the common suggestions of that post is to use the Bloc pattern so I will post the problems with choosing architecture without much clarity of how it works, so I will try to come up with some breakable app that will be hard with bloc pattern to build. again It will be from my perspective I am not a genius in architecture itself but what I have learned hard way is to customize the architecture based on my needs and the same goes for others that’s why there are many more architectures options to choose from.

Today we will go through some of my findings of how Observatory works. I will be posting much more than this article regarding flutter in upcoming parts, so stay tuned.

One of the problems that we face during development is debugging like say for an example,

We have some piece of conditions and now we don’t know which code did execute due to which this error has been generated. This is a basic feature of Observatory given by the Dart team (around 3–5 years ago). We can also execute commands there like assign value to a variable by debugging the line or piece of code.

After we run your app in Profile mode: read here how to run in profile mode

We can get the observatory link in console log after we run our app. something like below

[  +11 ms] Connecting to service protocol: http://127.0.0.1:54347/KDw3Qw6z9mE=/
[ +219 ms] Successfully connected to service protocol: http://127.0.0.1:54347/KDw3Qw6z9mE=/

And once we open the link you will be having something like this:

Dart Observatory

This is VM or We can say Virtual machine which is getting data from the device itself (just like realm chrome inspector). and we can be able to see different options here like timeline, debug, class hierarchy, CPU profile, allocation profile, heap snapshot, heap map, metrics, logging, etc. some of which are yet to explore from my side.

Isolates are like a thread and We just have one Isolate here as we see in Observatory. If we had a bunch of them we might be able to get the Isolate list there.

Read more from here for Isolates and how Dart is single-threaded.

Open up that Isolate main and scroll a little we will see something like this:

Observatory

Here we can check which line executed and which not as an example runApp method line is green which shows that it is executed while red lines indicate that it is not executed.
We have an error widget (customized screen if anything goes wrong we can see something like below image rather than red screen which we have by default in flutter).

To check the green and reds let’s change code a little bit

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return null;
}
}

Now due to this change, we will be able to see the custom error screen

custom error screen

and in Observatory, we will get

Observatory

Now the Error widget has been executed and shown in green while My app not as it has not been properly executed so it’s not in green.

Although this is not what we want to see right? brace up your selves we will see something amazing today.

This is the gist link for the current UI:

What we have for this case is the horizontal list inside a vertical list and we have done some nasty code in that.

//our build method
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Title of the page"),
),
body: buildBody(),
);
}
//build body
buildBody() {
var children = List.generate(
100000,
(pos) {
return Card(
child: Column(
children: <Widget>[
buildCarousel(),
Center(child: Text("Position: $pos")),
],
),
);
},
);
//this is our verticle list
return ListView(
shrinkWrap: true,
children: children,
);
}
//horizontal list
buildCarousel(){
var carousel = List.generate(
10,
(pos) {
return SizedBox(
width: 200,
height: 200,
child: Card(
color: hex(pos),
child: Image.network(
"your web url",
width: 200,
height: 200,
),
),
);
},
);
var carouselList = Container(
height: 200,
child: ListView(
scrollDirection: Axis.horizontal,
children: carousel,
),
);
return carouselList;
}

and hex function is like this:

hex(position){
if(position%2 ==0 ){
return HexColor("#0${position%2}b${position%2}c${position%2}");
}
return HexColor('#aabbcc');
}

in which we are giving different colors and creating new objects every time.

Now go the Observatory and check for allocation profile something like below

Now for an unknown reason(or might be I’m on the master branch to check Flutter web/desktop), we can not scroll this thing so click on the compact now to check things out and order size in descending.

So what do we have, Image instance is around 1000… something fine as we are generating a hardcoded list than ListView.Builder function which is bad in practice but just for the example let’s go on with it. Next are card, network image, sized box, list, oneByteString, HexColor, list view… wait, hold on why HexColor having this much of objects and do we need it? the answer is yes as we are converting hex code to RGB format color so we will be needing color object which is HexColor. But do we need to create it? that much item I mean sure we will be having colors but 1000000 that much don’t think so update the code and return constant colors as much as possible and reduce allocations right away.

There’s also another object that we need to look at is _OneByteString which is not known class so right-click and open it in another tab. check the functions listing:

_OneByteString.*
_OneByteString.==
...
_OneByteString._concatAll
_OneByteString._isWhitespace
_OneByteString._setAt
_OneByteString._splitWithCharCode
_OneByteString._substringUncheckedNative
_OneByteString.codeUnitAt
_OneByteString.split
_OneByteString.toLowerCase
_OneByteString.toUpperCase
//we can also check which class it's extends to:
class _OneByteString extends _StringBase {

After reading that much we conclude that it’s about string class and some operations. to check why is it allocating that much we need to go to the CPU profiler.

That will look something like this. and check for _OneByteString and click it. In which we are doing string manipulations for color hex and then hex to integer conversion inside HexColor class _getColorFromHex method which we ideally should not do and so replace with some performant code.

Sometimes it’s hard to find a problem as it is having a stack trace and we need to traverse through the call trace bottom-up so we need to search for what we want to check like if we are looking for string search for a string or if we are looking for paint search for paint.

I know it is going to be an impenetrable chart as it’s a heck of a lot stack trace but by allocations and some other methods provided by tools we will eventually figure out what’s going on.

There are also other things in performance we check for which is debug layers and frames.

We can check frames in this example and go to another tutorial for debugging layers or redundant layers to draw a performance graph.

Flutter is all about 60–120fps which has already been on the news thanks to the Flutter IO. In android/Ios if we can say screen/UI having some jank effect or rendering is not smooth. Then it’s related to the draw frames that the system is skipping due to intensive work on the main thread or complex calculations on the GPU thread.

Flutter is also not an exception even if you do async-await it’s not going to create a separate thread and so we always need to make UI minimal drawing. and if it’s computational work then create Isolate or use compute function for non UI related stuff and return just like await for async function call.

UI performance graph

We can turn on the UI performance on the device itself using a performance graph included in the tools or can check on the Flutter Performance tab. The graphs on the device are separated into two segments one is for GPU and another one is for UI. The black horizontal line indicates a 16ms time frame. if our app’s bar goes above it it is doing some intensive work on the respective thread. If the bar is even turned to red then it has skipped some frames to render the UI.

In the android studio Flutter Performance tab, they are showing red bars for every frame which could not be able to render in 16ms timeframe. Like here we have a 30frames frame rate which is equivalent to twice the frame of 16ms and so when we need to draw one frame we are just drawing half frame for that period time.

There are different calculations for the time frame and fps here is where you can check: https://www.youtube.com/watch?v=M8jGSkACneE&t=259s

There are a few articles which I like for the performance test and other stuff here are some reference:

  1. https://medium.com/flutter/performance-testing-of-flutter-apps-df7669bb7df7
  2. https://flutter.dev/docs/development/tools/devtools/timeline
  3. https://flutter.dev/docs/cookbook/testing/integration/profiling
  4. https://www.youtube.com/watch?v=GL61CotxCmM&t=199s
  5. https://medium.com/flutter/profiling-flutter-applications-using-the-timeline-a1a434964af3
  6. https://www.youtube.com/watch?v=y39pZCExsOs
  7. https://www.youtube.com/watch?v=Jn5ooFCPPr0
  8. https://medium.com/@parthdave93/single-thread-dart-what-ccbca2543ae9

I hope you liked it. Although it’s hard to find problems in the graph these references might help you with, some of them are informative some of them are abstract just like the flutter.dev documentation is abstract documentation.
And as they say, there are one or more ways to win a game, same goes for the performance we don’t need to check allocation if we take care of our code and architecture seriously and we also don’t need to check UI performance if you turn on the graph at the time of development.

Do you know you can press the clap👏 button 50 times? The higher you go, the more it motivates me to write more stuff for you guys!

Hello, I am Parth Dave. A noob developer and noob photographer. You can find me on Linkedin or stalk me on GitHub or maybe follow me on Twitter?

Have a nice fluttery day!

--

--

Dave Parth

Developer, Photographer. Flutter and Automation enthusiast.