2016-02-14 118 views
4

我從this answerforwhile循環在C#中有學問:「編譯器/ JIT對這種情況下的優化,只要您在使用arr.Length條件:」編譯器的邊界檢查/ JIT優化Java中環和C++

for(int i = 0 ; i < arr.Length ; i++) { 
    Console.WriteLine(arr[i]); // skips bounds check 
} 

這個誘惑我懷疑,如果java編譯器有這樣的優化。

for(int i=0; i<arr.length; i++) { 
    System.out.println(arr[i]); // is bounds check skipped here? 
} 

我認爲它的確如此嗎?使用CollectionArrayList時會發生同樣的情況嗎?


但是如果我不得不使用的myList.size()值身體的for循環裏面,現在正在考慮myList是一個ArrayList?所以在那種情況下不會myList.size()幫忙,因爲size()是一個方法調用嗎?例如可以是這樣的:

int len = myList.size(); // hoisting for using inside the loop 
for(int i = 0; i < myList.size(); i++) { // not using hoisted value for optimization 
    System.out.println(myList.get(i)); 

    if(someOtherVariable == len) { 
     doSomethingElse(); 
    } 
} 

編輯: 雖然我還沒有得到Java的一個答案,我仍然將第二部分對這一問題。

問: C++(C++ 98/C++ 11)有這樣的優化,例如:爲vector.size()string.size()?例如,哪種性能更好?

for (int i = 0; i < myvector.size(); ++i) 
    cout << myvector[i] << " "; // is bounds checking skipped here, like in C#? 

// does this manual optimisation help improve performance, or does it make? 
int size = myvector.size(); 
for (int i = 0; i < size; ++i) 
    cout << myvector[i] << " "; 

這就是說,沒有這樣的優化std::string存在呢?

+5

不用擔心;無論如何,方法調用通常都會內聯。 – SLaks

+0

@SLaks可以請你詳細介紹一下內聯嗎? –

+3

http://stackoverflow.com/q/1546694/34397 – SLaks

回答

2

的Java

由於Java 7,編譯器消除邊界爲基本數組檢查它是否可以證明外的綁定訪問是不可能的。在Java 7之前,JIT或AOT編譯器可以做到這一點。對於JIT和AOT編譯器,它不限於for (int i = 0; i < arr.length; i++),它可以在循環外部移動邊界檢查以查找諸如for (int i = 0; i < 10000000; i++)之類的循環,從而將其減少爲單個檢查。如果該檢查失敗,它將運行代碼的一個版本,並進行全面檢查以在正確的位置拋出異常。

對於集合,它更加複雜,因爲邊界由被調用的方法檢查,而不是在調用的地方。一般來說,它不能從字節碼中消除,但JIT和AOT編譯器如果可以內聯這些方法(取決於對象如何實例化以及存儲位置等),可以將其消除,因爲Java中的所有非私有方法是虛擬的,所以編譯器需要確保它不需要虛擬調用),但我不知道他們是否真的這樣做。

C++

C++不檢查邊界在operator []。它使用at檢查邊界。at是內聯的,因此它取決於特定的編譯器及其標誌,但通常情況下,編譯器可以刪除邊界檢查,以檢查它是否可以證明無法訪問是不可能的。它也可以在循環外移動邊界檢查,但它仍然需要保證異常將被拋出到正確的位置,所以我不知道是否有。

假設您在for循環之後不使用int size,那麼C++的兩個示例在編譯器中都會與Dead Code Elimination進行等效。如果您使用某些未內聯的方法,後者可能會更快。 (您也可以將size的定義放入初始化:for (int i = 0, size = myvector.size(); i < size; ++i)