Android Performance Tips

来源:互联网 发布:搜狐网络大厦邮政编码 编辑:程序博客网 时间:2024/05/18 05:40

http://developer.android.com/training/articles/perf-tips.html


Performance Tips



1.      Avoid Creating Unnecessary Objects

2.      Prefer Static Over Virtual

3.      Use Static Final For Constants

4.      Avoid Internal Getters/Setters

5.      Use Enhanced For Loop Syntax

6.      Consider Package Instead of Private Access with Private Inner Classes

7.      Avoid Using Floating-Point

8.      Know and Use the Libraries

9.      Use Native Methods Carefully

10.  Use Native Methods Judiciously

11.  Closing Notes


In this document

This document primarily coversmicro-optimizations that can improve overall app performance when combined, butit's unlikely that these changes will result in dramatic performance effects.Choosing the right algorithms and data structures should always be yourpriority, but is outside the scope of this document. You should use the tips inthis document as general coding practices that you can incorporate into yourhabits for general code efficiency.

There are two basic rules for writingefficient code:

·        Don't do work that you don't need to do.

·        Don't allocate memory if you can avoid it.

One of the trickiest problems you'll facewhen micro-optimizing an Android app is that your app is certain to be runningon multiple types of hardware. Different versions of the VM running ondifferent processors running at different speeds. It's not even generally thecase that you can simply say "device X is a factor F faster/slower thandevice Y", and scale your results from one device to others. Inparticular, measurement on the emulator tells you very little about performanceon any device. There are also huge differences between devices with and withouta JIT: the best code for a device with a JIT isnot always the best code for a device without.

To ensure your app performs well across awide variety of devices, ensure your code is efficient at all levels andagressively optimize your performance.

Avoid CreatingUnnecessary Objects


Object creation is never free. Agenerational garbage collector with per-thread allocation pools for temporaryobjects can make allocation cheaper, but allocating memory is always moreexpensive than not allocating memory.

As you allocate more objects in your app,you will force a periodic garbage collection, creating little"hiccups" in the user experience. The concurrent garbage collectorintroduced in Android 2.3 helps, but unnecessary work should always be avoided.

Thus, you should avoid creating objectinstances you don't need to. Some examples of things that can help:

·        If you have a method returning a string, andyou know that its result will always be appended to aStringBuffer anyway, changeyour signature and implementation so that the function does the appenddirectly, instead of creating a short-lived temporary object.

·        When extracting strings from a set of inputdata, try to return a substring of the original data, instead of creating acopy. You will create a newString object, but itwill share thechar[] with the data. (The trade-off being that ifyou're only using a small part of the original input, you'll be keeping it allaround in memory anyway if you go this route.)

A somewhat more radical idea is to slice upmultidimensional arrays into parallel single one-dimension arrays:

·        An array ofints is a muchbetter than an array ofInteger objects, butthis also generalizes to the fact that two parallel arrays of ints are also a lot more efficient than an array of(int,int) objects. Thesame goes for any combination of primitive types.

·        If you need to implement a container thatstores tuples of(Foo,Bar) objects, try to remember that two parallelFoo[] and Bar[] arrays aregenerally much better than a single array of custom(Foo,Bar) objects. (Theexception to this, of course, is when you're designing an API for other code toaccess. In those cases, it's usually better to make a small compromise to thespeed in order to achieve a good API design. But in your own internal code, youshould try and be as efficient as possible.)

Generally speaking, avoid creatingshort-term temporary objects if you can. Fewer objects created meanless-frequent garbage collection, which has a direct impact on user experience.

Prefer Static OverVirtual


If you don't need to access an object'sfields, make your method static. Invocations will be about 15%-20% faster. It'salso good practice, because you can tell from the method signature that callingthe method can't alter the object's state.

Use Static Final ForConstants


Consider the following declaration at thetop of a class:

static int intVal = 42;
static String strVal = "Hello, world!";

The compiler generates a class initializermethod, called<clinit>, that is executed when the class is firstused. The method stores the value 42 intointVal, and extracts areference from the classfile string constant table forstrVal. When thesevalues are referenced later on, they are accessed with field lookups.

We can improve matters with the"final" keyword:

static final int intVal = 42;
static final String strVal = "Hello, world!";

The class no longer requires a <clinit> method, becausethe constants go into static field initializers in the dex file. Code thatrefers tointVal will use the integer value 42 directly, andaccesses tostrVal will use a relatively inexpensive"string constant" instruction instead of a field lookup.

Note: Thisoptimization applies only to primitive types andString constants, notarbitrary reference types. Still, it's good practice to declare constants static final wheneverpossible.

Avoid InternalGetters/Setters


In native languages like C++ it's commonpractice to use getters (i = getCount()) instead ofaccessing the field directly (i = mCount). This is anexcellent habit for C++ and is often practiced in other object orientedlanguages like C# and Java, because the compiler can usually inline the access,and if you need to restrict or debug field access you can add the code at anytime.

However, this is a bad idea on Android.Virtual method calls are expensive, much more so than instance field lookups.It's reasonable to follow common object-oriented programming practices and havegetters and setters in the public interface, but within a class you shouldalways access fields directly.

Without a JIT, direct field access is about 3x faster than invoking a trivial getter.With the JIT (where direct field access is as cheap as accessing a local),direct field access is about 7x faster than invoking a trivial getter.

Note that if you're using ProGuard, you can have the best of both worlds becauseProGuard can inline accessors for you.

Use Enhanced ForLoop Syntax


The enhanced for loop (alsosometimes known as "for-each" loop) can be used for collections thatimplement theIterable interface andfor arrays. With collections, an iterator is allocated to make interface callsto hasNext() andnext(). With anArrayList, a hand-writtencounted loop is about 3x faster (with or without JIT), but for othercollections the enhanced for loop syntax will be exactly equivalent to explicititerator usage.

There are several alternatives for iteratingthrough an array:

static class Foo {
    int mSplat;
}

Foo[] mArray = ...

public void zero() {
    int sum = 0;
    for (int i = 0; i < mArray.length; ++i) {
        sum += mArray[i].mSplat;
    }
}

public void one() {
    int sum = 0;
    Foo[] localArray = mArray;
    int len = localArray.length;

    for (int i = 0; i < len; ++i) {
        sum += localArray[i].mSplat;
    }
}

public void two() {
    int sum = 0;
    for (Foo a : mArray) {
        sum += a.mSplat;
    }
}

zero() is slowest, becausethe JIT can't yet optimize away the cost of getting the array length once forevery iteration through the loop.

one() is faster. It pullseverything out into local variables, avoiding the lookups. Only the arraylength offers a performance benefit.

two() is fastest fordevices without a JIT, and indistinguishable fromone() for devices with a JIT. It uses the enhanced for loopsyntax introduced in version 1.5 of the Java programming language.

So, you should use the enhanced for loop by default,but consider a hand-written counted loop for performance-criticalArrayList iteration.

Tip: Also see JoshBloch'sEffective Java, item 46.

Consider PackageInstead of Private Access with Private Inner Classes


Consider the following class definition:

public class Foo {
    private class Inner {
        void stuff() {
            Foo.this.doStuff(Foo.this.mValue);
        }
    }

    private int mValue;

    public void run() {
        Inner in = new Inner();
        mValue = 27;
        in.stuff();
    }

    private void doStuff(int value) {
        System.out.println("Value is " + value);
    }
}

What's important here is that we define aprivate inner class (Foo$Inner) that directlyaccesses a private method and a private instance field in the outer class. Thisis legal, and the code prints "Value is 27" as expected.

The problem is that the VM considers directaccess toFoo's private members fromFoo$Inner to be illegalbecauseFoo and Foo$Inner are differentclasses, even though the Java language allows an inner class to access an outerclass' private members. To bridge the gap, the compiler generates a couple ofsynthetic methods:

/*package*/ static int Foo.access$100(Foo foo){
    return foo.mValue;
}
/*package*/ static void Foo.access$200(Foo foo, int value) {
    foo.doStuff(value);
}

The inner class code calls these staticmethods whenever it needs to access themValue field or invokethedoStuff() method in the outer class. What this means is that thecode above really boils down to a case where you're accessing member fieldsthrough accessor methods. Earlier we talked about how accessors are slower thandirect field accesses, so this is an example of a certain language idiomresulting in an "invisible" performance hit.

If you're using code like this in aperformance hotspot, you can avoid the overhead by declaring fields and methodsaccessed by inner classes to have package access, rather than private access.Unfortunately this means the fields can be accessed directly by other classesin the same package, so you shouldn't use this in public API.

Avoid UsingFloating-Point


As a rule of thumb, floating-point is about2x slower than integer on Android-powered devices.

In speed terms, there's no differencebetween float and double on the moremodern hardware. Space-wise,double is 2x larger. Aswith desktop machines, assuming space isn't an issue, you should preferdouble to float.

Also, even for integers, some processorshave hardware multiply but lack hardware divide. In such cases, integerdivision and modulus operations are performed in software—something to thinkabout if you're designing a hash table or doing lots of math.

Know and Use theLibraries


In addition to all the usual reasons toprefer library code over rolling your own, bear in mind that the system is atliberty to replace calls to library methods with hand-coded assembler, whichmay be better than the best code the JIT can produce for the equivalent Java.The typical example here is String.indexOf() and relatedAPIs, which Dalvik replaces with an inlined intrinsic. Similarly, theSystem.arraycopy() method is about9x faster than a hand-coded loop on a Nexus One with the JIT.

Tip: Also see JoshBloch'sEffective Java, item 47.

Use Native MethodsCarefully


Developing your app with native code usingthe Android NDK isn't necessarily more efficient than programmingwith the Java language. For one thing, there's a cost associated with the Java-nativetransition, and the JIT can't optimize across these boundaries. If you'reallocating native resources (memory on the native heap, file descriptors, orwhatever), it can be significantly more difficult to arrange timely collectionof these resources. You also need to compile your code for each architectureyou wish to run on (rather than rely on it having a JIT). You may even have tocompile multiple versions for what you consider the same architecture: nativecode compiled for the ARM processor in the G1 can't take full advantage of theARM in the Nexus One, and code compiled for the ARM in the Nexus One won't runon the ARM in the G1.

Native code is primarily useful when youhave an existing native codebase that you want to port to Android, not for"speeding up" parts of your Android app written with the Javalanguage.

If you do need to use native code, youshould read ourJNI Tips.

Tip: Also see JoshBloch'sEffective Java, item 54.

Performance Myths


On devices without a JIT, it is true thatinvoking methods via a variable with an exact type rather than an interface isslightly more efficient. (So, for example, it was cheaper to invoke methods onaHashMap map than aMap map, even though inboth cases the map was aHashMap.) It was not thecase that this was 2x slower; the actual difference was more like 6% slower.Furthermore, the JIT makes the two effectively indistinguishable.

On devices without a JIT, caching fieldaccesses is about 20% faster than repeatedly accessing the field. With a JIT,field access costs about the same as local access, so this isn't a worthwhileoptimization unless you feel it makes your code easier to read. (This is trueof final, static, and static final fields too.)

Always Measure


Before you start optimizing, make sure youhave a problem that you need to solve. Make sure you can accurately measureyour existing performance, or you won't be able to measure the benefit of thealternatives you try.

Every claim made in this document is backedup by a benchmark. The source to these benchmarks can be found in thecode.google.com "dalvik" project.

The benchmarks are built with the Calipermicrobenchmarking framework for Java. Microbenchmarks are hard to get right, soCaliper goes out of its way to do the hard work for you, and even detect somecases where you're not measuring what you think you're measuring (because, say,the VM has managed to optimize all your code away). We highly recommend you useCaliper to run your own microbenchmarks.

You may also find Traceview useful for profiling, but it's important to realizethat it currently disables the JIT, which may cause it to misattribute time tocode that the JIT may be able to win back. It's especially important aftermaking changes suggested by Traceview data to ensure that the resulting codeactually runs faster when run without Traceview.

For more help profiling and debugging yourapps, see the following documents:

·        Profiling with Traceview and dmtracedump

·        Analyzing UI Performance with Systrace

 

 


0 0
原创粉丝点击