为什么具有多个嵌套循环的代码可以在 GCC 上立即完成,但在 VS 上需要很长时间
Why code with multiple nested loops can finish immediately on GCC but take forever on VS?
long long r = 0;
long long k = 0;
for (; k < 9999999999999; k++)
{
for (long long i = 0; i < 9999999999999; i++)
{
for (long long j = 0; j < 9999999999999; j++)
{
r = (r + (i * j) % 100) % 47;
if (r != 0)
{
r++;
}
}
}
}
此代码在 i3Core 上以 0.000001 秒的墙壁秒执行,在 i7Core 上检查boost::timer::auto_cpu_timer
。
但是在Visual Studio 2010中,它似乎在无限的时间内运行。
GCC 或 VS 有什么问题?GCC 优化是否过多?
是的,GCC 正在优化该代码。
具体来说,它知道您没有使用结果,因此它会删除所有结果。
(您从不使用变量 r
。
这称为死代码消除。
为了防止编译器对其进行优化,您需要以某种方式使用结果。尝试在最后打印r
:
cout << r << endl;
但是,我警告您需要减少迭代次数,否则它可能不会在您的有生之年完成。
我刚刚在VS2010 x64中对此进行了测试。从组件来看,很明显VS2010无法优化整个循环。
这表明不同的编译器优化不同事物的能力各不相同。
相关且更深入:GCC 如何优化循环内递增的未使用变量?
您正在运行未定义的行为
在大多数平台上,您的代码在i * j
具有未定义的行为,因为:
9999999999999 * 9999999999999 > 2^64
long long
在大多数平台上都是2^64
,因为对 128 位整数的硬件支持太少。
感谢 GCC -O3 在警告中指出这一点:它可以假设 UB 不会发生并且运行循环的次数更少。另请参阅:为什么此循环产生"警告:迭代 3u 调用未定义的行为"并输出超过 4 行?
所以任何事情都可能发生。
让我们反编译它,看看到底发生了什么
海湾合作委员会 4.8:
gcc -c -g -std=c99 -O0 a.c
objudmp -S a.o
输出:
a.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
int main() {
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
long long r = 0;
4: 48 c7 45 e0 00 00 00 movq $0x0,-0x20(%rbp)
b: 00
long long k = 0;
c: 48 c7 45 e8 00 00 00 movq $0x0,-0x18(%rbp)
13: 00
for (; k < 9999999999999; k++)
14: e9 f8 00 00 00 jmpq 111 <main+0x111>
{
for (long long i = 0; i < 9999999999999; i++)
19: 48 c7 45 f0 00 00 00 movq $0x0,-0x10(%rbp)
20: 00
21: e9 d2 00 00 00 jmpq f8 <main+0xf8>
{
for (long long j = 0; j < 9999999999999; j++)
26: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
2d: 00
2e: e9 ac 00 00 00 jmpq df <main+0xdf>
{
r = (r + (i * j) % 100) % 47;
33: 48 8b 45 f0 mov -0x10(%rbp),%rax
37: 48 0f af 45 f8 imul -0x8(%rbp),%rax
3c: 48 89 c1 mov %rax,%rcx
3f: 48 ba 0b d7 a3 70 3d movabs $0xa3d70a3d70a3d70b,%rdx
46: 0a d7 a3
49: 48 89 c8 mov %rcx,%rax
4c: 48 f7 ea imul %rdx
4f: 48 8d 04 0a lea (%rdx,%rcx,1),%rax
53: 48 c1 f8 06 sar $0x6,%rax
57: 48 89 c2 mov %rax,%rdx
5a: 48 89 c8 mov %rcx,%rax
5d: 48 c1 f8 3f sar $0x3f,%rax
61: 48 29 c2 sub %rax,%rdx
64: 48 89 d0 mov %rdx,%rax
67: 48 c1 e0 02 shl $0x2,%rax
6b: 48 01 d0 add %rdx,%rax
6e: 48 8d 14 85 00 00 00 lea 0x0(,%rax,4),%rdx
75: 00
76: 48 01 d0 add %rdx,%rax
79: 48 c1 e0 02 shl $0x2,%rax
7d: 48 29 c1 sub %rax,%rcx
80: 48 89 ca mov %rcx,%rdx
83: 48 8b 45 e0 mov -0x20(%rbp),%rax
87: 48 8d 0c 02 lea (%rdx,%rax,1),%rcx
8b: 48 ba 99 5c 41 4c ae movabs $0x572620ae4c415c99,%rdx
92: 20 26 57
95: 48 89 c8 mov %rcx,%rax
98: 48 f7 ea imul %rdx
9b: 48 c1 fa 04 sar $0x4,%rdx
9f: 48 89 c8 mov %rcx,%rax
a2: 48 c1 f8 3f sar $0x3f,%rax
a6: 48 29 c2 sub %rax,%rdx
a9: 48 89 d0 mov %rdx,%rax
ac: 48 89 45 e0 mov %rax,-0x20(%rbp)
b0: 48 8b 55 e0 mov -0x20(%rbp),%rdx
b4: 48 89 d0 mov %rdx,%rax
b7: 48 01 c0 add %rax,%rax
ba: 48 01 d0 add %rdx,%rax
bd: 48 c1 e0 04 shl $0x4,%rax
c1: 48 29 d0 sub %rdx,%rax
c4: 48 29 c1 sub %rax,%rcx
c7: 48 89 c8 mov %rcx,%rax
ca: 48 89 45 e0 mov %rax,-0x20(%rbp)
if (r != 0)
ce: 48 83 7d e0 00 cmpq $0x0,-0x20(%rbp)
d3: 74 05 je da <main+0xda>
{
r++;
d5: 48 83 45 e0 01 addq $0x1,-0x20(%rbp)
long long k = 0;
for (; k < 9999999999999; k++)
{
for (long long i = 0; i < 9999999999999; i++)
{
for (long long j = 0; j < 9999999999999; j++)
da: 48 83 45 f8 01 addq $0x1,-0x8(%rbp)
df: 48 b8 fe 9f 72 4e 18 movabs $0x9184e729ffe,%rax
e6: 09 00 00
e9: 48 39 45 f8 cmp %rax,-0x8(%rbp)
ed: 0f 8e 40 ff ff ff jle 33 <main+0x33>
int main() {
long long r = 0;
long long k = 0;
for (; k < 9999999999999; k++)
{
for (long long i = 0; i < 9999999999999; i++)
f3: 48 83 45 f0 01 addq $0x1,-0x10(%rbp)
f8: 48 b8 fe 9f 72 4e 18 movabs $0x9184e729ffe,%rax
ff: 09 00 00
102: 48 39 45 f0 cmp %rax,-0x10(%rbp)
106: 0f 8e 1a ff ff ff jle 26 <main+0x26>
int main() {
long long r = 0;
long long k = 0;
for (; k < 9999999999999; k++)
10c: 48 83 45 e8 01 addq $0x1,-0x18(%rbp)
111: 48 b8 fe 9f 72 4e 18 movabs $0x9184e729ffe,%rax
118: 09 00 00
11b: 48 39 45 e8 cmp %rax,-0x18(%rbp)
11f: 0f 8e f4 fe ff ff jle 19 <main+0x19>
125: b8 00 00 00 00 mov $0x0,%eax
r++;
}
}
}
}
}
12a: 5d pop %rbp
12b: c3 retq
所以所有的循环似乎都存在。
-O3
:
0000000000000000 <main>:
0: 31 c0 xor %eax,%eax
2: c3 retq
所以它显然被优化了。C 标准中没有任何内容阻止这种优化,因此 GCC 会这样做。
如果我们像Mystical提到的那样在末尾添加一个printf("%lld", r);
,即使使用-O3
,也确实需要很长时间,并且生成的优化代码与未优化的代码几乎相同。
,它变得更有趣:编译器是否允许消除无限循环?
如何防止如果发生:如何防止GCC优化繁忙的等待循环?
- 使用Boost Interprocess创建托管共享内存需要很长时间
- SFML RenderWindow打开窗口需要很长时间
- Kafka C++客户端需要很长时间才能收到消息
- 给定使用 C++ 或 C,我如何测量在 linux 下进行线程切换需要多长时间?可能吗?
- asio::read() 需要很长时间,使用 asio::write 没有问题
- 将线程锁定很长时间
- 正在等待在非阻塞文件描述符上长时间运行ioctl
- 即使长时间等待,C++线程也不会加入
- 连接() 在连接被拒绝时长时间挂起
- 为什么这段代码需要这么长时间才能用 g++ 编译?
- 如何在长时间运行的方法中等待信号?
- mbed 套接字连接需要很长时间
- Lambda 捕获此函数和长时间运行的函数
- std::fstream 需要很长时间才能将大数据写入.csv文件中
- 为什么与Java和Python相比,使用Cmake运行C++程序每次都需要这么长时间?
- 需要很长时间
- 在关闭应用程序期间正确关闭线程,该线程可能会运行很长时间的循环
- 如何在长时间计算中进行C 的一次进度更新
- VS C++:树状 if/else 结构的代码生成需要很长时间
- 为什么具有多个嵌套循环的代码可以在 GCC 上立即完成,但在 VS 上需要很长时间