Designing for Performance 筆記

 

 


Avoid Creating Unnecessary Objects

在 Dev Guide 裡還有另外一個重要的觀念:避免產生不必要的物件。這個觀念真的很重要,如果要讓程式的效能更好,不斷在 Main Thread 裡產生物件是 bad idea。原因是 Garbage Collection。

Android 的 Dalvik 虛擬機在初始化時,會建立 Main Thread。Main Thread 負責眾多任務,其中之一是 Garbage Collection,也就是負責做資源回收,把用不到的物件回收再利用。如果我們不斷在 Main Thread 裡產生物件,Main Thread 很可能忙著幫我們收拾垃圾,當 Main Thread 花費太多心思幫我們撿垃圾,處理其它工作的效率就會變差,例如:UI 事件處理。

所以,這告訴我們一個重要的觀念:「不要產生不必要的物件」,難怪 Dev Guide 在講「Designing for Performance」時,把這個觀念放在首位。有些 Class 在設計時,提供了多種 instantiate(實例化)方法,以 android.os.Message 為例,我們可以這樣做:

Message msg = new Message();

也可以這樣做:

Message msg = Message.obtain();

(From Jollen blog): http://www.jollen.org/blog/2011/05/android-leak-3-dont-create-unnecessary-object-in-main-thread.html

 

Prefer Static Over Virtual

If you don’t need to access an object’s fields, make your method static. Invocations will be about 15%-20% faster.

 

 

Avoid Internal Getters/Setters

for (int i = 0; i < this.getCount(); i++)

dumpItems(this.getItem(i))

在for迴圈作this.getCount()這種操作!很浪費!請先宣告個變數,再放到for迴圈裡面

Use Static Final For Constants

the constants go into static field initializers in the dex file it’s good practice to declare constants static final whenever possible.

Use Enhanced For Loop Syntax

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()最慢,因為JIT並不能優化循環中每次迭代獲取數組長度的消耗。
one()快一點,因為一切都放入局部變量,避免了字段查找。只有在數組長度上提供了性能上的優勢。
two()在設備沒有JIT的時候最快,在有JIT的時候和one()大相徑庭。其使用了Java 1.5 介紹的增強型for循環語法。

總結:默認使用增強性for循環,但是考慮在關鍵性能地方使用傳統for循環來迭代ArrayList。

 

Consider Package Instead of Private Access with Private Inner Classes

考慮下面的類定義:

public class Foo {
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);
}

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

這裡需要指出的關鍵是我們定義的內部類可以直接訪問外部類的私有方法和私有實例字段,這是合法的,並且代碼將輸出期盼的值“Value is 27”。

但問題是,即使Java 語言允許內部類可以直接訪問外部類的私有成員,VM(虛擬機)認為從Foo$Inner類直接訪問Foo的私有成員是非法的,因為Foo和Foo$Inner是不同的類。為了彌補這樣的差距,編譯器生成了一對綜合的方法:
/*package*/ static int Foo.access$100(Foo foo) {
return foo.mValue;
}
/*package*/ static void Foo.access$200(Foo foo, int value) {
foo.doStuff(value);
}

當內部類需要訪問外部類的“mValue”或者“doStuff”方法時將調用這些靜態方法。上面的代碼就意味著使用了訪問方法替代了你直接訪問成員字段。之前我們提到了訪問器是如何的比直接訪問慢,所以這完全是一個因為語言習慣(idiom)導致的“不可見”性能損失。
我們可以通過為內部類訪問的字段或者方法定義為包可見而不是私有的。這樣將運行的更快並且會不再生成上面的代碼。 (遺憾的是,這也意味隻字段可以被同一個包中的其他類直接訪問,這樣與將所有字段聲明為private的做法背道而馳,再次提醒,如果你在設計定義公共的API,你應​​該小心考慮使用這種優化)。

Use Floating-Point Judiciously

As a rule of thumb, floating-point is about 2x slower than integer on Android devices.

Know And Use The Libraries

In addition to all the usual reasons to prefer library code over rolling your own

Use Native Methods Judiciously

Native code isn’t necessarily more efficient than Java.

 

#即時編譯(Just-in-time compilation,又稱為動態翻譯,是一種提高程序運行效率的方法。通常,程序有兩種運行方式:靜態編譯與動態直譯。 靜態編譯的程序在執行前全部被翻譯為機器碼,而直譯執行的則是一句一句邊運行邊翻譯。Just-in-time compilatio則混合了這二者,一句一句編譯原始碼,但是會將翻譯過的代碼緩存起來以降低性能損耗。相對於靜態編譯代碼,即時編譯的代碼可以處理延遲綁定並增強安全性。

發表迴響