Something that any application should be concerned with is performance. One way to look at performance is via profiling. Most IDEs and platforms provide tools to profile your application to take a look at detailed system information about various parts of your application. With Android applications you can execute an adb
command to generate a very thorough snapshot of the graphics information and that’s what we’re going to look at in this post.
Profiling is the analysis that measures the memory or complexity of a program, the usage of the program, or the frequency and duration of function calls. In simple terms it is analyzing how your program is performing on the system it is executing on.
You will need to have adb
configured on your machine since we’ll be using the Android Debug Bridge to generate reports for our application.
You will also need to know your android application’s package name, this would be something like com.instagram.android. If you are unsure of the Android app’s package name you can get this from the AndroidManifest.xml or the projects gradle file.
While this isn’t exactly profiling it is useful to determine the graphics info of your application. If you want to profile the CPU, RAM, network, etc. during execution, Android Studio provides an excellent profiler to dig deep into the system.
Once adb
is setup and we know the application’s package name. Execute the following command to generate a system report for our process. We are going to use Instagram as our example here.
adb shell dumpsys gfxinfo com.instagram.android > layout-profile.txt
Let’s look at this command so we understand what it’s doing.
First, we are executing a dumpsys
and since we are interested in the performance related to our app’s UI we are passing gfxinfo
which is for graphics information.
Then you’ll notice com.instagram.android
(Instagram’s package name), we are providing this argument to the command because we are only interested in the data for one process and not the entire system.
Last you’ll notice > layout-profile.txt
this is writing the output from the command to a txt file in the directory we execute this command.
Applications Graphics Acceleration Info:
Uptime: 537251619 Realtime: 836306786
** Graphics info for pid 2094 [com.instagram.android] **
Stats since: 530111479831959ns
Total frames rendered: 35466
Janky frames: 1167 (3.29%)
50th percentile: 5ms
90th percentile: 7ms
95th percentile: 11ms
99th percentile: 25ms
Number Missed Vsync: 141
Number High input latency: 7859
Number Slow UI thread: 314
Number Slow bitmap uploads: 0
Number Slow issue draw commands: 136
Number Frame deadline missed: 470
HISTOGRAM: 5ms=29796 6ms=1651 7ms=831 8ms=440 9ms=390 10ms=397 11ms=258 12ms=177 13ms=124 14ms=76 15ms=87 16ms=148 17ms=331 18ms=131 19ms=75 20ms=57 21ms=38 22ms=32 23ms=24 24ms=28 25ms=25 26ms=23 27ms=15 28ms=7 29ms=12 30ms=9 31ms=7 32ms=15 34ms=18 36ms=22 38ms=17 40ms=17 42ms=13 44ms=11 46ms=14 48ms=22 53ms=8 57ms=19 61ms=19 65ms=6 69ms=12 73ms=13 77ms=10 81ms=3 85ms=3 89ms=1 93ms=3 97ms=2 101ms=1 105ms=0 109ms=2 113ms=1 117ms=5 121ms=1 125ms=1 129ms=0 133ms=3 150ms=11 200ms=3 250ms=1 300ms=0 350ms=0 400ms=0 450ms=0 500ms=0 550ms=0 600ms=0 650ms=0 700ms=0 750ms=0 800ms=0 850ms=0 900ms=0 950ms=0 1000ms=0 1050ms=0 1100ms=0 1150ms=0 1200ms=0 1250ms=0 1300ms=0 1350ms=0 1400ms=0 1450ms=0 1500ms=0 1550ms=0 1600ms=0 1650ms=0 1700ms=0 1750ms=0 1800ms=0 1850ms=0 1900ms=0 1950ms=0 2000ms=0 2050ms=0 2100ms=0 2150ms=0 2200ms=0 2250ms=0 2300ms=0 2350ms=0 2400ms=0 2450ms=0 2500ms=0 2550ms=0 2600ms=0 2650ms=0 2700ms=0 2750ms=0 2800ms=0 2850ms=0 2900ms=0 2950ms=0 3000ms=0 3050ms=0 3100ms=0 3150ms=0 3200ms=0 3250ms=0 3300ms=0 3350ms=0 3400ms=0 3450ms=0 3500ms=0 3550ms=0 3600ms=0 3650ms=0 3700ms=0 3750ms=0 3800ms=0 3850ms=0 3900ms=0 3950ms=0 4000ms=0 4050ms=0 4100ms=0 4150ms=0 4200ms=0 4250ms=0 4300ms=0 4350ms=0 4400ms=0 4450ms=0 4500ms=0 4550ms=0 4600ms=0 4650ms=0 4700ms=0 4750ms=0 4800ms=0 4850ms=0 4900ms=0 4950ms=0
Font Cache (CPU):
Size: 664.05 kB
Glyph Count: 60
CPU Caches:
GPU Caches:
Other:
Buffer Object: 63.00 KB (2 entries)
Texture: 32.00 KB (1 entry)
Image:
Texture: 5.95 MB (41 entries)
Scratch:
Buffer Object: 64.00 KB (2 entries)
RenderTarget: 1.27 MB (5 entries)
Texture: 3.00 MB (2 entries)
Other Caches:
Current / Maximum
VectorDrawableAtlas 0.00 kB / 0.00 KB (entries = 0)
Layers Total 0.00 KB (numLayers = 0)
Total GPU memory usage:
10879652 bytes, 10.38 MB (7.35 MB is purgeable)
Pipeline=Skia (OpenGL)
Layout Cache Info:
Usage: 5000/5000 entries
Hit ratio: 191887/199638 (0.961175)
Profile data in ms:
com.instagram.android/com.instagram.android.activity.MainTabActivity/android.view.ViewRootImpl@c358bce (visibility=0)
AtchDlg:com.instagram.android/com.instagram.android.activity.MainTabActivity/android.view.ViewRootImpl@2b97aef (visibility=0)
View hierarchy:
com.instagram.android/com.instagram.android.activity.MainTabActivity/android.view.ViewRootImpl@c358bce
398 views, 346.06 kB of display lists
AtchDlg:com.instagram.android/com.instagram.android.activity.MainTabActivity/android.view.ViewRootImpl@2b97aef
1 views, 0.84 kB of display lists
Total ViewRootImpl: 2
Total Views: 399
Total DisplayList: 346.90 kB
No doubt that is a lot to digest and we don’t have the source to modify and play around to see what optimizations we could make so let’s try with a NativeScript application. We’ll generate the report with the basic “Hello World” sample and then we’ll add other views to the page and see how that affects memory usage.
Assuming you have the NativeScript CLI installed, execute tns create profile
from your terminal to generate a bare “Hello World” NativeScript application. Run the app with tns run android
in the root of the project and then execute the gfxinfo
command using the package name for our NativeScript application.
It should be adb shell dumpsys gfxinfo org.nativescript.profile > layout-profile.txt
unless you didn’t use profile
for the app name when creating. The report in the text file will look similar to the output below:
Applications Graphics Acceleration Info:
Uptime: 537741987 Realtime: 836797155
** Graphics info for pid 16911 [org.nativescript.profile] **
Stats since: 537701663540996ns
Total frames rendered: 6
Janky frames: 2 (33.33%)
50th percentile: 9ms
90th percentile: 250ms
95th percentile: 250ms
99th percentile: 250ms
Number Missed Vsync: 1
Number High input latency: 5
Number Slow UI thread: 1
Number Slow bitmap uploads: 0
Number Slow issue draw commands: 1
Number Frame deadline missed: 1
HISTOGRAM: 5ms=3 6ms=0 7ms=0 8ms=0 9ms=1 10ms=0 11ms=0 12ms=0 13ms=0 14ms=0 15ms=0 16ms=0 17ms=0 18ms=0 19ms=0 20ms=0 21ms=0 22ms=0 23ms=0 24ms=0 25ms=0 26ms=0 27ms=0 28ms=0 29ms=0 30ms=0 31ms=0 32ms=0 34ms=0 36ms=0 38ms=0 40ms=0 42ms=0 44ms=0 46ms=0 48ms=0 53ms=0 57ms=0 61ms=0 65ms=0 69ms=0 73ms=0 77ms=0 81ms=0 85ms=0 89ms=0 93ms=0 97ms=1 101ms=0 105ms=0 109ms=0 113ms=0 117ms=0 121ms=0 125ms=0 129ms=0 133ms=0 150ms=0 200ms=0 250ms=1 300ms=0 350ms=0 400ms=0 450ms=0 500ms=0 550ms=0 600ms=0 650ms=0 700ms=0 750ms=0 800ms=0 850ms=0 900ms=0 950ms=0 1000ms=0 1050ms=0 1100ms=0 1150ms=0 1200ms=0 1250ms=0 1300ms=0 1350ms=0 1400ms=0 1450ms=0 1500ms=0 1550ms=0 1600ms=0 1650ms=0 1700ms=0 1750ms=0 1800ms=0 1850ms=0 1900ms=0 1950ms=0 2000ms=0 2050ms=0 2100ms=0 2150ms=0 2200ms=0 2250ms=0 2300ms=0 2350ms=0 2400ms=0 2450ms=0 2500ms=0 2550ms=0 2600ms=0 2650ms=0 2700ms=0 2750ms=0 2800ms=0 2850ms=0 2900ms=0 2950ms=0 3000ms=0 3050ms=0 3100ms=0 3150ms=0 3200ms=0 3250ms=0 3300ms=0 3350ms=0 3400ms=0 3450ms=0 3500ms=0 3550ms=0 3600ms=0 3650ms=0 3700ms=0 3750ms=0 3800ms=0 3850ms=0 3900ms=0 3950ms=0 4000ms=0 4050ms=0 4100ms=0 4150ms=0 4200ms=0 4250ms=0 4300ms=0 4350ms=0 4400ms=0 4450ms=0 4500ms=0 4550ms=0 4600ms=0 4650ms=0 4700ms=0 4750ms=0 4800ms=0 4850ms=0 4900ms=0 4950ms=0
Font Cache (CPU):
Size: 35.45 kB
Glyph Count: 17
CPU Caches:
GPU Caches:
Other:
Buffer Object: 63.00 KB (2 entries)
Scratch:
Buffer Object: 64.00 KB (2 entries)
RenderTarget: 7.44 MB (1 entry)
Texture: 1.00 MB (1 entry)
Other Caches:
Current / Maximum
VectorDrawableAtlas 0.00 kB / 0.00 KB (entries = 0)
Layers Total 0.00 KB (numLayers = 0)
Total GPU memory usage:
8977408 bytes, 8.56 MB (7.56 MB is purgeable)
Pipeline=Skia (OpenGL)
Layout Cache Info:
Usage: 13/5000 entries
Hit ratio: 39/52 (0.750000)
Profile data in ms:
org.nativescript.profile/com.tns.NativeScriptActivity/android.view.ViewRootImpl@f1ce416 (visibility=0)
View hierarchy:
org.nativescript.profile/com.tns.NativeScriptActivity/android.view.ViewRootImpl@f1ce416
15 views, 13.73 kB of display lists
Total ViewRootImpl: 1
Total Views: 15
Total DisplayList: 13.73 kB
Let’s make a small UI change to our app by modifying the main-page.xml to have the following markup:
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onNavigatingTo" class="page">
<Page.actionBar>
<ActionBar title="My App" icon="" class="action-bar">
</ActionBar>
</Page.actionBar>
<StackLayout class="p-20">
<Label text="Tap the button" class="h1 text-center"/>
<Button text="TAP" tap="{{ onTap }}" class="btn btn-primary btn-active"/>
<GridLayout rows="*, *, *" columns="*, *, *, *">
<Image src="res://icon" row="0" col="0" stretch="none" />
<Image src="res://icon" row="0" col="1" stretch="none" />
<Image src="res://icon" row="0" col="2" stretch="none" />
<Image src="res://icon" row="0" col="3" stretch="none" />
<Button row="1" col="0" text="YES" tap="" />
<Button row="1" col="1" text="YES" tap="" />
<Button row="1" col="2" text="YES" tap="" />
<Button row="1" col="3" text="YES" tap="" />
<Image row="2" col="0" src="https://secure.gravatar.com/avatar/c53602f132b37e1e904df9d7bc431732?s=160&d=identicon&r=g" stretch="none" />
<Image row="2" col="1" src="https://secure.gravatar.com/avatar/c53602f132b37e1e904df9d7bc431732?s=160&d=identicon&r=g" stretch="none" />
<Image row="2" col="2" src="https://secure.gravatar.com/avatar/c53602f132b37e1e904df9d7bc431732?s=160&d=identicon&r=g" stretch="none" />
<Image row="2" col="3" src="https://secure.gravatar.com/avatar/c53602f132b37e1e904df9d7bc431732?s=160&d=identicon&r=g" stretch="none" />
</GridLayout>
<Label text="{{ message }}" class="h2 text-center" textWrap="true"/>
</StackLayout>
</Page>
All we are doing is adding some simple images and buttons to the page to see the difference when running the gfxinfo
report which is below.
Applications Graphics Acceleration Info:
Uptime: 537919744 Realtime: 836974911
** Graphics info for pid 16911 [org.nativescript.profile] **
Stats since: 537701663540996ns
Total frames rendered: 12
Janky frames: 4 (33.33%)
50th percentile: 10ms
90th percentile: 133ms
95th percentile: 250ms
99th percentile: 250ms
Number Missed Vsync: 2
Number High input latency: 6
Number Slow UI thread: 2
Number Slow bitmap uploads: 0
Number Slow issue draw commands: 1
Number Frame deadline missed: 2
HISTOGRAM: 5ms=3 6ms=0 7ms=0 8ms=0 9ms=3 10ms=1 11ms=0 12ms=0 13ms=1 14ms=0 15ms=0 16ms=0 17ms=0 18ms=0 19ms=0 20ms=0 21ms=0 22ms=0 23ms=0 24ms=0 25ms=0 26ms=1 27ms=0 28ms=0 29ms=0 30ms=0 31ms=0 32ms=0 34ms=0 36ms=0 38ms=0 40ms=0 42ms=0 44ms=0 46ms=0 48ms=0 53ms=0 57ms=0 61ms=0 65ms=0 69ms=0 73ms=0 77ms=0 81ms=0 85ms=0 89ms=0 93ms=0 97ms=1 101ms=0 105ms=0 109ms=0 113ms=0 117ms=0 121ms=0 125ms=0 129ms=0 133ms=1 150ms=0 200ms=0 250ms=1 300ms=0 350ms=0 400ms=0 450ms=0 500ms=0 550ms=0 600ms=0 650ms=0 700ms=0 750ms=0 800ms=0 850ms=0 900ms=0 950ms=0 1000ms=0 1050ms=0 1100ms=0 1150ms=0 1200ms=0 1250ms=0 1300ms=0 1350ms=0 1400ms=0 1450ms=0 1500ms=0 1550ms=0 1600ms=0 1650ms=0 1700ms=0 1750ms=0 1800ms=0 1850ms=0 1900ms=0 1950ms=0 2000ms=0 2050ms=0 2100ms=0 2150ms=0 2200ms=0 2250ms=0 2300ms=0 2350ms=0 2400ms=0 2450ms=0 2500ms=0 2550ms=0 2600ms=0 2650ms=0 2700ms=0 2750ms=0 2800ms=0 2850ms=0 2900ms=0 2950ms=0 3000ms=0 3050ms=0 3100ms=0 3150ms=0 3200ms=0 3250ms=0 3300ms=0 3350ms=0 3400ms=0 3450ms=0 3500ms=0 3550ms=0 3600ms=0 3650ms=0 3700ms=0 3750ms=0 3800ms=0 3850ms=0 3900ms=0 3950ms=0 4000ms=0 4050ms=0 4100ms=0 4150ms=0 4200ms=0 4250ms=0 4300ms=0 4350ms=0 4400ms=0 4450ms=0 4500ms=0 4550ms=0 4600ms=0 4650ms=0 4700ms=0 4750ms=0 4800ms=0 4850ms=0 4900ms=0 4950ms=0
Font Cache (CPU):
Size: 37.09 kB
Glyph Count: 19
CPU Caches:
GPU Caches:
Other:
Buffer Object: 63.00 KB (2 entries)
Image:
Texture: 648.06 KB (8 entries)
Scratch:
Buffer Object: 64.00 KB (2 entries)
RenderTarget: 7.44 MB (1 entry)
Texture: 1.00 MB (1 entry)
Other Caches:
Current / Maximum
VectorDrawableAtlas 0.00 kB / 0.00 KB (entries = 0)
Layers Total 0.00 KB (numLayers = 0)
Total GPU memory usage:
9641024 bytes, 9.19 MB (8.19 MB is purgeable)
Pipeline=Skia (OpenGL)
Layout Cache Info:
Usage: 14/5000 entries
Hit ratio: 94/108 (0.870370)
Profile data in ms:
org.nativescript.profile/com.tns.NativeScriptActivity/android.view.ViewRootImpl@f1ce416 (visibility=0)
View hierarchy:
org.nativescript.profile/com.tns.NativeScriptActivity/android.view.ViewRootImpl@f1ce416
28 views, 24.74 kB of display lists
Total ViewRootImpl: 1
Total Views: 28
Total DisplayList: 24.74 kB
Now that we have a report with zero modifications to the “Hello World” template vs. a small change to add several images and buttons we can see that we increased the Total GPU memory usage, which is to be expected. We also increased the Total DisplayList, again normal and to be expected since more UI views were added to the page.
A key take away from the report is looking at the Total GPU memory usage data. I’ve worked on several applications that had layouts that were duplicating the device’s drawing effort to render go from 23+MB to ~10-12MB. Also remember that this concept applies to any target platform that renders a UI. The more UI to measure and draw, the more resources the machine will use to do so.
In this post we reviewed how to generate graphics information reports using the Android Debug Bridge (ADB) for Android applications. With the reports you can view many statistics related to the applications frame and graphics performance to determine how well the UI is functioning. This is scratching the surface of profiling the layouts of Android applications but it should provide enough insight to monitor your development and be aware of your app’s layouts and implementation.