I recently had a situation where I needed to change from LinearLayout to FlexboxLayout. Before I could implement this change I needed to be sure we didn't make the UI slower in any way. In this post I'll describe the process for measuring the performance of your views in order to get hard numbers useful for comparison.


GPU Profiler

In the official Android documentation you can find information on how to enable GPU profiling on an Android device or emulator. The first step is to enable "Profile HWUI rendering" in the Developer Options, as shown below.

How to enable profiling using ADB.

This dialog presents two profiling options, and you want to select the second one which lets you print the data using ADB. The first option in the dialog will display colored bars on top of your application, where you get an overview of how long each pass takes. While easy to use, it's not particularly useful for comparing the performance when doing changes to a layout in the same screen. Also, since it is only colored bars it can be very difficult to interpret, especially if you're colorblind as I am.

The testing section of the training documentation contains the information needed on how to use ADB to get the exact numbers from the GPU profiler. Once you've read that page, you'll know that in order to get the numbers of the last 120 frames for a specific app (this is the amount of frame recorded by the GPU profiler), you need to run the following ADB command:

adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats
ADB command for printing the framestats information for a specific app

This will, among a bunch of other stats, print a comma separated list of numbers. These are the nanosecond timestamps for each of the phases in the rendering of a frame (up to the last 120 frames).

Flags,IntendedVsync,Vsync,OldestInputEvent,NewestInputEvent,HandleInputStart,AnimationStart,PerformTraversalsStart,DrawStart,SyncQueued,SyncStart,IssueDrawCommandsStart,SwapBuffers,FrameCompleted,DequeueBufferDuration,QueueBufferDuration,GpuCompleted,
0,27965466202353,27965466202353,27965449758000,27965461202353,27965467153286,27965471442505,27965471925682,27965474025318,27965474588547,27965474860786,27965475078599,27965479796151,27965480589068,
0,27965482993342,27965482993342,27965465835000,27965477993342,27965483807401,27965486875630,27965487288443,27965489520682,27965490184380,27965490568703,27965491408078,27965496119641,27965496619641,
0,27965499784331,27965499784331,27965481404000,27965494784331,27965500785318,27965503736099,27965504201151,27965506776568,27965507298443,27965507515005,27965508405474,27965513495318,27965514061984,
0,27965516575320,27965516575320,27965497155000,27965511575320,27965517697349,27965521276151,27965521734797,27965524350474,27965524884536,27965525160578,27965526020891,27965531371203,27965532114484,
Example of the CSV output from the GPU profiler

The easiest way to work with this is to copy it all and paste it into Google Sheets. See the trick below for how to paste CSV data into columns.

After pasting CSV data, click on the clipboard pop-up menu and select ‘Split text to columns’

Interpret framestat data

Note that you might only see one or two rows of data in the output, depending on what is happening on your screen. Simple views where you're not scrolling or doing any animations is one example of this. You might want to run the app several times to get multiple values for your measurement.

The first number in each row is a flag that indicates if this is a valid measurement or not. 0 is a valid measurement, whereas anything else denotes a frame that was rendered during a transition between two activities or some other event that you're not interested in.

Next, there are two intervals we want to collect: the measure/layout pass and draw pass. These will tell you how well your view is performing. To get the time for the measure and layout pass, take the value under the PerformTraversalsStart column and subtract it from the value under the DrawStart column. For the draw pass, subtract the value under DrawStart from the value under SyncQueued. You now have the time for how long each of these took.

Calculating the layout/measure and draw pass using Google Sheets

All the values are in nanoseconds, so don't be alarmed if it looks very big. Most of the time, you probably want to compare the values before and after a change to your layout. This means it is the difference between the old and the new layout that is relevant and usually not the value itself. If the new one is faster, or at least as fast, everything is good. If it's slower, you probably need to do some optimizations.

Conclusions

The GPU profiler in Android is very useful, but only for certain scenarios. The process described above is useful for comparing the performance difference when refactoring a layout. There is plenty of other information coming from the profiler that can be useful, but which I'm not covering in this post.

Sometimes you probably don't even need to measure the performance difference. If you have a layout consisting of multiple nested LinearLayout or RelativeLayout, and you managed to replace them with a single ConstraintLayout, there is probably no point in spending time on measuring this.

In my case, I needed to change from a LinearLayout to a FlexboxLayout due to a bug in Right-To-Left rendering. After comparing the performance between the two layouts I actually found that FlexboxLayout performed even better than LinearLayout. A nice side-effect for a rather cumbersome refactoring!

Thanks to Ataul, Viktor and Danny for help with reviewing this post!