2015-10-05 69 views
5

我目前正在審查包含此一PullRequest:避免間接和冗餘的方法調用

-  for (int i = 0; i < outgoingMassages.size(); i++) { 
+  for (int i = 0, size = outgoingMassages.size(); i < size; i++) 

https://github.com/criticalmaps/criticalmaps-android/pull/52

莫名其妙地感覺我錯了 - 會認爲虛擬機在做這些的優化 - 但不能真的很確定。如果這種改變是有道理的,我們很樂意得到一些輸入 - 或者確認這是在VM端完成的。

回答

5

沒有,是不是肯定VM將代碼從

-  for (int i = 0; i < outgoingMassages.size(); i++) { 

改變

+  for (int i = 0, size = outgoingMassages.size(); i < size; i++) 

在你的循環有可能的是,outgoingMassages會改變它的大小。所以這個優化不能被JVM應用。如果這是共享資源,另一個線程可以更改outgoingMassages大小。

只有在行爲沒有改變時,JVM才能更改代碼。例如,它可以用一系列的字符串連接替換爲StringBuilder,或者它可以內聯一個簡單的方法調用,或者如果它是一個常數值,可以從循環中計算出一個值。

+0

如果我們知道'outgoingMassages.size()'不會改變?是不是會優化,因爲我們不需要每次調用對象'outgoingMassages'和方法'size()'? – HendraWD

+0

@HendraWD如果您知道在將循環大小分配給變量的過程中,outgoingMessages的大小沒有發生變化,則可以進行優化。但是,通常最好專注於如何縮短循環內代碼的時間。 –

+0

好的,謝謝你的確認:) – HendraWD

1

虛擬機不會做這種優化。由於size()方法可能不會返回每次調用的相同結果。所以這個方法必須被稱爲每次迭代。

但是,如果size是一個簡單的getter-method,則對性能的影響非常小。可能不可衡量。 (在少數情況下,它可以使Java使用並行化,這可能會有所不同,但這取決於循環的內容)。

更大的區別可能是確保for循環具有預先已知的迭代量。在這個例子中,對我來說這似乎沒有意義。但也許被調用的方法可能會返回不需要的更改結果?

0

如果您的集合上的size()方法只是提供私有字段的值,那麼VM將優化大部分(但不是全部)。它會通過內聯size()方法來實現,以便它可以訪問該字段。

,將無法得到優化剩下的一點是,在新的代碼size將得到視爲final,因此恆定的,而從現場收集回升將不會被視爲final(也許它是從修改另一個線程)。因此,在最初的情況下,該字段將在每次迭代中讀取,但在新案例中不會。

0

很可能是任何像樣的優化器 - 無論是在虛擬機或編譯器 - 將認識到:

class Messages { 

    int size; 

    public int size() { 
     return size; 
    } 
} 

public void test() { 
    Messages outgoingMassages = new Messages(); 
    for (int i = 0; i < outgoingMassages.size(); i++) { 

    } 
} 

,並優化到

for (int i = 0; i < outgoingMassages.size; i++) { 

做多餘的 - 未經測試 - 優化應因此被認爲是evil

0

方法調用將發生在每個循環的迭代,並且是不是免費的成本。既然你無法預測這種情況發生的頻率,所以稱它爲一旦將永遠是減去。這是一個小的優化,但不應該依賴編譯器來爲你做優化。

此外,構件outgoingMassages ..

private ArrayList<OutgoingChatMessage> outgoingMassages .. 

...應該是一個接口:

private List<OutgoingChatMessage> outgoingMassages .. 

然後,主叫.size()將成爲虛擬方法。爲了找出具體的對象類,將爲層次結構的所有類調用方法表。這不再是免費的。