Java和C++中for循环边界检查的编译器/JIT优化

Sна*_*ƒаӽ 5 c++ java for-loop compiler-optimization c++11

我从得知这个答案forwhile在C#中循环,即:“编译/ JIT有这种情况,只要你使用最佳化arr.Length的状态:”

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

这让我想知道 java 编译器是否有这样的优化。

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

我认为是的,嗯,是吗?使用Collectionlike时会发生同样的情况ArrayList吗?


但是如果我必须在 for 循环myList.size() 体内部使用 的值,考虑现在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();
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑: 虽然我还没有获得 Java 的答案,但我仍在向这个问题添加第二部分。

问: C++ (C++98/C++11) 是否有这样的优化,例如 forvector.size()string.size()?例如,哪个在性能方面更好?

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

或者

// 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] << " ";
Run Code Online (Sandbox Code Playgroud)

也就是说,这种优化是否也存在std::string

Ste*_*oft 2

爪哇

从 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 []. 当您使用 时,它会检查边界atat是内联的,因此它取决于特定的编译器及其标志,但通常,如果编译器可以证明不可能进行越界访问,则可以删除边界检查。它还可以将边界检查移到循环之外,但它仍然需要保证异常将在正确的位置抛出,所以我不知道是否有异常。

int size假设您在循环后不使用,C++ 的两个示例在执行死代码消除的编译器中是等效的for。如果您使用一些非内联的方法,后者可能会更快。(你也可以把 的定义size放在初始化里面for (int i = 0, size = myvector.size(); i < size; ++i):)