2010-12-03 45 views
7

我已經創建了一個類來處理我的調試輸出,這樣我就不需要在發佈之前去掉所有的日誌輸出。Android:運行一個空方法會產生多少開銷?

public class Debug { 
    public static void debug(String module, String message) { 
     if(Release.DEBUG) 
      Log.d(module, message); 
    } 
} 

讀另外一個問題,我已經瞭解到,如果常數Release.DEBUG是假的if語句的內容不會被編譯。

我想知道的是運行此空方法會產生多少開銷? (一旦if子句被刪除,方法中就沒有代碼了)它是否會對我的應用程序有任何影響?顯然,性能是寫手機時的一個大問題= P

感謝

加里

回答

14

測量的Nexus S的做採用Android 2.3.2:

10^6 iterations of 1000 calls to an empty static void function: 21s <==> 21ns/call 
10^6 iterations of 1000 calls to an empty non-static void function: 65s <==> 65ns/call 

10^6 iterations of 500 calls to an empty static void function: 3.5s <==> 7ns/call 
10^6 iterations of 500 calls to an empty non-static void function: 28s <==> 56ns/call 

10^6 iterations of 100 calls to an empty static void function: 2.4s <==> 24ns/call 
10^6 iterations of 100 calls to an empty non-static void function: 2.9s <==> 29ns/call 

控制:

10^6 iterations of an empty loop: 41ms <==> 41ns/iteration 
10^7 iterations of an empty loop: 560ms <==> 56ns/iteration 
10^9 iterations of an empty loop: 9300ms <==> 9.3ns/iteration 

我反覆測量幾次。沒有發現顯着偏差。 你可以看到每個呼叫的成本可以根據工作負載(可能是由於JIT編譯)相差很大, 但3組可以得出結論:

  1. 的Dalvik/Java的吸在優化死代碼

  2. 靜態函數調用可以被優化比非靜態 (非靜態函數是虛擬的,需要在虛擬表中查找)

  3. 在Nexus S的成本不超過70ns的更大/呼叫好得多(那~70 cpu週期) 並且與一個空循環迭代的成本相當(即,一個增量和一個局部變量的條件檢查)

觀察到,在你的情況下,字符串參數將始終被評估。如果你做字符串連接,這將涉及創建中間字符串。這將是非常昂貴的,涉及到很多gc。例如執行功能:

void empty(String string){ 
} 

調用參數如

empty("Hello " + 42 + " this is a string " + count); 

10^4次迭代的100個這樣的呼叫需要10秒。這是10us /次,即比空的呼叫慢1000倍。它也會產生大量的GC活動。避免這種情況的唯一方法是手動內聯函數,即使用>> if語句而不是調試函數調用。這是醜陋的,但是讓它工作的唯一方法。

+0

是啊如果你沒有註釋的話,它確實是咬你的stringbuilder。原因是運行時jit或aot編譯器無法提前確定字符串創建是否會失敗,從而影響程序的流程。 一個如果在它周圍是混亂。預編譯器是正確的方式,但現在沒有簡單的方法來做到這一點與Android默認工具,我認爲。 – 2016-04-30 17:13:29

2

除非你從一個深度嵌套的循環中調用這個,我就不會擔心。

2

一個好的編譯器刪除了整個空方法,根本沒有任何開銷。我不確定Dalvik編譯器是否已經做到了這一點,但我懷疑它很可能,至少從Froyo的Just-in-time編譯器到來之後。

參見:Inline expansion

+0

給自己一個apktool的副本,反編譯你的應用程序,看看它是否被優化出了達爾維克字節碼。後來的階段可以在安裝或運行時加載時進行優化,但這似乎是最明顯的階段。 – 2010-12-03 14:09:06

+0

如果你反編譯apk,你實際上沒有看到art或dalvik優化,你只會看到java編譯器和dex轉換器(也可能是proguard)所做的優化。您需要從內部提取緩存的內容。 – 2016-04-30 17:10:10

2

在性能方面產生其獲得通過進入調試功能的消息的開銷將是一個很多更嚴重,因爲它有可能它們的內存分配,例如

Debug.debug(mymodule, "My error message" + myerrorcode); 

即使通過郵件分類,仍然會發生這種情況。 不幸的是,如果你的目標是性能,你真的需要圍繞調用這個函數的「if(Release.DEBUG)」,而不是在函數本身內部,你會在很多android代碼中看到這個。

1

這是一個有趣的問題,我喜歡@misiu_mp分析,所以我想我會在運行Android 6.0.1的Nexus 7上進行2016測試更新。下面是測試代碼:

public void runSpeedTest() { 
    long startTime; 
    long[] times = new long[100000]; 
    long[] staticTimes = new long[100000]; 
    for (int i = 0; i < times.length; i++) { 
     startTime = System.nanoTime(); 
     for (int j = 0; j < 1000; j++) { 
      emptyMethod(); 
     } 
     times[i] = (System.nanoTime() - startTime)/1000; 
     startTime = System.nanoTime(); 
     for (int j = 0; j < 1000; j++) { 
      emptyStaticMethod(); 
     } 
     staticTimes[i] = (System.nanoTime() - startTime)/1000; 
    } 
    int timesSum = 0; 
    for (int i = 0; i < times.length; i++) { timesSum += times[i]; Log.d("status", "time," + times[i]); sleep(); } 
    int timesStaticSum = 0; 
    for (int i = 0; i < times.length; i++) { timesStaticSum += staticTimes[i]; Log.d("status", "statictime," + staticTimes[i]); sleep(); } 
    sleep(); 
    Log.d("status", "final speed = " + (timesSum/times.length)); 
    Log.d("status", "final static speed = " + (timesStaticSum/times.length)); 
} 

private void sleep() { 
    try { 
     Thread.sleep(10); 
    } catch (InterruptedException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
} 

private void emptyMethod() { } 
private static void emptyStaticMethod() { } 

sleep()加入到防止溢出Log.d緩衝器。

我發揮它周圍很多次,結果與@misiu_mp相當一致:

10^5 iterations of 1000 calls to an empty static void function: 29ns/call 
10^5 iterations of 1000 calls to an empty non-static void function: 34ns/call 

靜態方法調用總是比非靜態方法調用稍微快一點,但它會出現一個)自Android 2.3.2以來,差距已經明顯縮小,並且b)調用靜態或空閒方法仍有成本。

然而,看時間直方圖會發現一些有趣的事情。絕大多數呼叫,無論是否靜止,都需要30-40ns,並且仔細查看數據,它們實際上完全是30ns。

enter image description here

運行帶有空環(註釋出方法調用)相同的代碼產生爲8ns的平均速度,但是,關於所測量的時間的3/4是爲0ns,而其餘部分是完全爲30ns。

我不確定如何解釋這些數據,但我不確定@ misiu_mp的結論是否成立。空靜態和非靜態方法之間的差異可以忽略不計,而測量的優勢恰恰是30ns。這就是說,似乎運行空方法仍然存在一些非零成本。

相關問題