c++ - 为什么现代 C++ 编译器不优化这样的简单循环? (Clang,MSVC)

标签 c++ visual-c++ gcc clang compiler-optimization

当我使用 Clang (-O3) 或 MSVC (/O2) 编译和运行此代码时...

#include <stdio.h>
#include <time.h>

static int const N = 0x8000;

int main()
{
    clock_t const start = clock();
    for (int i = 0; i < N; ++i)
    {
        int a[N];    // Never used outside of this block, but not optimized away
        for (int j = 0; j < N; ++j)
        {
            ++a[j];  // This is undefined behavior (due to possible
                     // signed integer overflow), but Clang doesn't see it
        }
    }
    clock_t const finish = clock();
    fprintf(stderr, "%u ms\n",
        static_cast<unsigned int>((finish - start) * 1000 / CLOCKS_PER_SEC));
    return 0;
}

...循环没有得到优化。

此外,既没有 Clang 3.6 也没有 Visual C++ 2013 也没有 GCC 4.8.1 告诉我该变量未初始化!

现在我意识到缺乏优化本身并不是一个错误,但考虑到现在编译器应该非常聪明,我发现这令人惊讶。这似乎是一段如此简单的代码,即使是十年前的 active 分析技术也应该能够优化变量 a 并因此优化整个循环——更不用说递增的事实了该变量已经是未定义的行为。

然而只有 GCC 能够确定它是一个空操作,并且没有一个编译器告诉我这是一个未初始化的变量。

这是为什么?是什么阻止了简单的 active 分析告诉编译器 a 未使用?此外,为什么编译器一开始就没有检测到 a[j] 未初始化?为什么所有这些编译器中现有的未初始化变量检测器都无法捕捉到这个明显的错误?

最佳答案

未定义的行为在这里无关紧要。将内部循环替换为:

    for (int j = 1; j < N; ++j)
    {
        a[j-1] = a[j];
        a[j] = j;
    }

... 具有相同的效果,至少对于 Clang。

问题是内部循环既从 a[j] 加载(对于某些 j)又存储到 a[j] (对于某些 j)。不能删除任何存储,因为编译器认为它们可能对以后的加载可见,并且不能删除任何加载,因为它们的值被使用(作为以后存储的输入)。结果,循环仍然对内存有副作用,所以编译器看不到它可以被删除。

与 n.m. 的回答相反,将 int 替换为 unsigned 不会使问题消失。 Clang 3.4.1 生成的代码using intusing unsigned int是一样的。

关于c++ - 为什么现代 C++ 编译器不优化这样的简单循环? (Clang,MSVC),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25151508/

相关文章:

c++ - 概念和模板约束之间有什么区别?

c++ - boost::asio,为什么我的套接字在调用 async_receive_from 后立即运行回调函数?

c++ - 我需要调用 CloseHandle 吗?

c++ - 传递包含 SSE/AVX 值的类型

c - memcpy 在不同的编译器中给出不同的输出

c++ - 如何从 C++ 中的线程访问非静态方法使用 ref 类

visual-c++ - 如何编写编译器可以高效编译为 SSE 或 AVX 的 c++ 代码?

gcc/ld 链接时不遵循符号链接(symbolic link)文件

编译器声称指针在未使用时为 const

c++ - Qt中文本的定位规则到底是什么?