2016-02-14 80 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)