Android-Developers

  • Subscribe to our RSS feed.
  • Twitter
  • StumbleUpon
  • Reddit
  • Facebook
  • Digg

Thursday, 14 October 2010

Traceview War Story

Posted on 12:07 by Unknown

I recently took my first serious look at Traceview, and it occurred to me, first, that there are probably a few other Android developers who haven’t used it and, second, that this is an opportunity to lecture sternly on one of my favorite subjects: performance improvement and profiling. This is perhaps a little bit Android-101; If you already know all about Traceview, you can stop here and go back to coding.

Making Apps Fast

Here’s a belief that I think I share with most experienced developers: For any app that is even moderately complex, you’re not smart enough to predict what the slow parts are going to be, because nobody is smart enough to predict where software bottlenecks will turn up.

So the smart way to write a fast app is to build it in the simplest way that could possibly work, avoiding egregiously-stupid thing like order-N-squared algorithms and doing I/O on the Android UI thread. Who knows, it might be fast enough, and then you’re done!

If it isn’t fast enough, don’t guess why. Measure it and find out, using a profiler. Actually I’ve been known to do this, when backed into a corner, using things like System.err.println("Entered at" + System.currentTimeMillis()); Fortunately, Android comes with a reasonably decent profiler, so you don’t have to get ugly like that.

Case Study: LifeSaver 2

I have this little utility in Android Market called LifeSaver 2, the details are on my personal blog. At one point, it reads the SMS and phone-call logs out of the system and persists them in a JSON text file on the SD card. Since this is kind of slow, it shows a nice dynamic progress bar. It occurred to me to wonder why it was kind of slow to write a few hundred records into a text file on a device that, after all, has a gigahertz processor.

Somebody who foolishly disregarded my advice above might assume that the slowdown had to be due to the ContentProvider Cursor machinery reading the system logs, or failing that, the overhead of writing to the SD card. A wiser person would instrument the code and find out. Let’s do that.

Turning On Tracing

I went into Saver.java and bracketed the code in its run() method like so:

       public void run() {

android.os.Debug.startMethodTracing("lsd");

// ... method body elided

android.os.Debug.stopMethodTracing();
}

The first call turns tracing on, the argument "lsd" (stands for Life Saver Debug, of course) tells the system to put the trace log in /sdcard/lsd.trace. Remember that doing this means you have to add the WRITE_EXTERNAL_STORAGE permission so you can save the trace info; don‘t forget to remove that before you ship.

[Update:] Android engineer Xavier Ducrohet writes to remind me: “DDMS has a start/stop profiling button in the ‘device view’. Upon clicking stop it launches TraceView with the trace file. This is not as fine grained as putting start/stopMethodTracing in your code but can be quite useful. For VMs earlier than froyo, the permission is required as well (DDMS basically automate getting the trace from the sd card and saving it locally before calling traceview). For Froyo+ VMs, the VM is able to send the trace file through the JDWP connection and the permission is not needed anymore (which is really useful).” Thanks, Xav!

Then you run your app, then you copy the output over to your computer, and fire up Traceview.

540> adb pull /sdcard/lsd.trace
541> traceview lsd

At this point, you will have noticed three things. First, turning tracing on really slows down your app. Second, the tracefile is big; in this case, 8.6M for a run that took like four seconds. Third, that traceview looks pretty cool.

The bars across the top show the app’s threads and how they dealt out the time; since the Nexus One is single-threaded CPU, they have to take turns. Let’s zero in on one 100-msec segment.

The top line is where my app code is running (the red segment is GC happening), the middle line is the UI thread and the bursts of activity are the ProgressBar updating, and I have no idea what the third thread, named HeapWorker, does, but it doesn’t seem a major contributor to the app’s runtime, so let’s ignore it.

The bottom of the screen is where the really interesting data is; it shows which of your methods burned the time, and can be sorted in a bunch of different ways. Let’s zero in on the first two lines.

Translated into English, this tells us that the top-level routine consumed 100% of the time if you include everything it called (well, yeah), but only 0.9% of the time itself. The next line suddenly starts to get real interesting: java.io.PrintStream.println(Object) and whatever it calls are using 65.2% of the app’s time. This is the code that writes the JSON out to the SD card. Right away, we know that apparently the task of pulling the data out of the phone’s ContentProviders doesn’t seem to be very expensive; it’s the output that’s hurting.

Can we conclude that the app is limited by the sluggish write performance of the SD card? Let’s drill down, which is done in the most obvious point-and-click way imaginable.

Ooh, there’s a nasty surprise. Of course, println calls (in effect) toString() on all its arguments. It looks like turning the arguments to strings is taking over half the time, before it even dispatches from println(Object) to println(String).

I’ll skip the step of drilling down into println(String) but it does suggest that yes, there is some slow I/O happening there, to the SD card. But let’s look inside that String.valueOf() call.

There’s your smoking pistol. It turns out that org.json.JSONObject.toString() is what we professional programmers call a, well, this is a family-friendly operation so I won’t go there. You can poke around inside it, but it’s just depressing.

What you can do, however, is sort all the routines by their “Exclusive” times, as in the number of CPU circles burned right there in the routine. Here are all of them that use 1% or more of the total execution time.

There’s a little bit of GC and Android framework View-wrangling stuff in there, but the display is dominated by org.jason and java.lang.StringBuilder code.

The Conclusion

The real conclusion is that in the case of this app, I actually don’t care about the performance. A user runs it a grand total of two times, once on the old phone and once on the new phone, and it’s got lots of eye candy, so I just don’t think there’s a problem.

If I did want to speed this up, it’s obvious what to do. First, either stop using JSON, or find a cheaper way to serialize it. Second, do fewer println() calls; glom the data together in one big buffer and just blast it out with a single I/O call. But, and here’s the key point, if I’d guessed where the bottlenecks were, I’d have been wrong, mostly.

Traceview is a nice tool, and if you don’t already know it, you owe it to yourself to learn it.

Email ThisBlogThis!Share to XShare to FacebookShare to Pinterest
Posted in | No comments
Newer Post Older Post Home

0 comments:

Post a Comment

Subscribe to: Post Comments (Atom)

Popular Posts

  • Bring Your Apps into the Classroom, with Google Play for Education
    Posted by Shazia Makhdumi, Head of Strategic EDU Partnerships, Google Play team Google Play for Education has officially launched . It’s an ...
  • A Faster Emulator with Better Hardware Support
    [This post is by Xavier Ducrohet and Reto Meier of the Android engineering team. — Tim Bray.] The Android emulator is a key tool for Android...
  • Powering Chrome to Phone with Android Cloud to Device Messaging
    [This post is by Dave Burke, who's an Engineering Manager 80% of the time. — Tim Bray] Android Cloud to Device Messaging (C2DM) was lau...
  • Android 1.5 is here!
    I've got some good news today: the Android 1.5 SDK, release 1 is ready! Grab it from the download page . For an overview of the new Andr...
  • Memory Analysis for Android Applications
    [This post is by Patrick Dubroy, an Android engineer who writes about programming, usability, and interaction on his personal blog . — Tim B...
  • Preview of Google TV Add-on for the Android SDK
    [This post is by Ambarish Kenghe, who’s a Product Manager for Google TV — Tim Bray] At Google I/O , we announced that Android Market is comi...
  • Android SDK Tools, Revision 20
    [This post is by Xavier Ducrohet , Tech Lead for the Android developer tools] Along with the preview of the Android 4.1 (Jelly Bean) platfo...
  • RenderScript Intrinsics
    Posted by R. Jason Sams , Android RenderScript Tech Lead RenderScript has a very powerful ability called Intrinsics . Intrinsics are built-...
  • In-App Billing on Android Market: Ready for Testing
    [This post is by Eric Chu, Android Developer Ecosystem. —Dirk Dougherty] Back in January we announced our plan to introduce Android Market ...
  • Twitter for Android: A closer look at Android’s evolving UI patterns
    [This post is by Chris Nesladek, Interaction Designer, Richard Fulcher, Interaction Designer, and Virgil Dobjanschi, Software Engineer — Ti...

Categories

  • accessibility
  • Action Bar
  • Administration
  • Android
  • Android 1.5
  • Android 1.6
  • Android 2.0
  • Android 2.1
  • Android 2.2
  • Android 2.3
  • Android 2.3.3
  • Android 3.0
  • Android 3.2
  • Android 4.0
  • Android 4.2
  • Android 4.3
  • Android 4.4
  • Android Design
  • Android Developer Challenge
  • Android Developer Phone
  • Android Market
  • Android SDK
  • Android Studio
  • Animation and Graphics
  • Announcements
  • App Components
  • App Resources
  • Apps
  • Audio
  • Authentication
  • Best Practices
  • Boston
  • Code Day
  • Connectivity
  • Content Provider
  • Cool Stuff
  • Dashboard
  • Daydream
  • Debugging
  • Developer Console
  • Developer Days
  • Developer Labs
  • Developer profiles
  • Developer Story
  • Education
  • Games
  • GCM
  • Gestures
  • Google Analytics
  • Google Cloud Messaging
  • Google Cloud Platform
  • Google I/O
  • Google Play
  • Google Play game services
  • Google Play services
  • Google Services
  • Google Wallet
  • Google+
  • Guidelines
  • How-to
  • Image Processing
  • IME
  • In-app Billing
  • Input methods
  • Intents
  • io2010
  • IO2013
  • JNI
  • Layout
  • Localization
  • Location
  • Location and Sensors
  • London
  • Maps
  • Media and Camera
  • Mountain View
  • Munich
  • NDK
  • Open source
  • OpenGL ES
  • Optimization
  • Performance
  • Photo Sphere
  • Promo Graphics
  • Quality
  • Quick Search Box
  • Renderscript
  • Resources
  • RTL
  • Sample code
  • SDK Tools
  • SDK updates
  • Security
  • Sensors
  • Speech Input
  • Support Library
  • Survey
  • Tablets
  • Tel Aviv
  • Telephony
  • Testing
  • Text and Input
  • Text-to-Speech
  • Tools
  • Touch
  • User Interface
  • User Support
  • WebView
  • Widgets

Blog Archive

  • ►  2013 (45)
    • ►  November (2)
    • ►  October (7)
    • ►  September (2)
    • ►  August (5)
    • ►  July (5)
    • ►  June (4)
    • ►  May (9)
    • ►  April (3)
    • ►  March (2)
    • ►  February (3)
    • ►  January (3)
  • ►  2012 (43)
    • ►  December (5)
    • ►  November (3)
    • ►  October (3)
    • ►  September (1)
    • ►  August (1)
    • ►  July (2)
    • ►  June (5)
    • ►  May (1)
    • ►  April (5)
    • ►  March (6)
    • ►  February (5)
    • ►  January (6)
  • ►  2011 (67)
    • ►  December (7)
    • ►  November (7)
    • ►  October (5)
    • ►  September (5)
    • ►  August (3)
    • ►  July (7)
    • ►  June (3)
    • ►  May (5)
    • ►  April (6)
    • ►  March (8)
    • ►  February (7)
    • ►  January (4)
  • ▼  2010 (72)
    • ►  December (8)
    • ►  November (3)
    • ▼  October (4)
      • Improving App Quality
      • Traceview War Story
      • The Five Steps to Future Hardware Happiness
      • Android Market New Country Roll-out Details
    • ►  September (8)
    • ►  August (6)
    • ►  July (9)
    • ►  June (11)
    • ►  May (11)
    • ►  April (2)
    • ►  March (3)
    • ►  February (2)
    • ►  January (5)
  • ►  2009 (63)
    • ►  December (7)
    • ►  November (5)
    • ►  October (5)
    • ►  September (8)
    • ►  August (2)
    • ►  July (1)
    • ►  June (2)
    • ►  May (5)
    • ►  April (12)
    • ►  March (5)
    • ►  February (8)
    • ►  January (3)
  • ►  2008 (40)
    • ►  December (3)
    • ►  November (1)
    • ►  October (4)
    • ►  September (6)
    • ►  August (4)
    • ►  June (1)
    • ►  May (5)
    • ►  April (4)
    • ►  March (5)
    • ►  February (2)
    • ►  January (5)
  • ►  2007 (8)
    • ►  December (3)
    • ►  November (5)
Powered by Blogger.

About Me

Unknown
View my complete profile