我有以下代码:
#include <iostream>
int main()
{
int n = 100;
long a = 0;
int *x = new int[n];
for(int i = 0; i < n; ++i)
for(int j = 0; j < n; ++j)
for(int k = 0; k < n; ++k)
for(int l = 0; l < n; ++l)
{
a += x[(j + k + l) % 100];
}
std::cout << a << std::endl;
delete[] x;
return 0;
}
如果我在没有优化的情况下编译g ++ test.cc然后运行时间./a.out它将显示0.7s。但是当我用-O编译它时,时间减少了2倍。
使用的编译器
g++ (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2
我的问题
如何重写代码,以便在没有-O的情况下编译时我可以获得相同的时间(或接近它)?换句话说,如何手动优化嵌套循环?
为什么我要求
我有一个类似的代码,如果我使用-O优化,运行速度大约快4倍。
PS:我查看了编译器的手册,但是有太多的标志来测试哪一个确实有所不同。
答案 0 :(得分:3)
此代码具有未定义的行为,因此实际上优化器可以做任何想做的事情。
a += x[j + k + l % 100];
应该是:
a += x[(j + k + l) % 100];
如果你解决了这个问题,我仍然不明白为什么你要优化实际上什么都不做的事情......
就个人而言,我会将其优化为::))
std::cout << 0 << std::endl;
注意:移除i
的循环并执行std::cout << a * n << std::endl;
答案 1 :(得分:3)
编译器使用-O优化的大部分内容都是低于C ++的级别。例如,所有变量都存在于内存中,包括循环变量。因此,如果没有优化,编译器很可能会在内部循环的每次迭代中首先读取循环变量以将其与0进行比较,在循环内部再次加载它以便将其用于索引,然后在循环结束时将再次读取该值,递增它并将其写回。通过优化,它会注意到循环体中的循环变量没有改变,因此不需要每次都从内存中重新读取。此外,它还会注意到变量的地址永远不会被采用,因此没有其他代码可以访问它,因此也可以省略将其写入存储器。也就是说,循环变量将仅存在于内存中。仅执行此优化将在执行函数期间节省三亿次内存读取和一亿次内存写入。但是由于处理器寄存器和内存读/写之类的东西没有在语言级别公开,因此无法在语言级别对其进行优化。
此外,手动优化编译器优化的东西是没有意义的。最好把时间花在优化编译器无法优化的事情上。
答案 2 :(得分:1)
你可以观察到你的循环形成一组系数coeff[i]
,这样单个循环求和a[i] * coeff[i]
产生与嵌套循环相同的总数。如果我们假设您的索引为(i + j + k) % 100
,则coeff[i] = n * n * n
为i
,那么您的整个程序将简化为
long coeff = n * n * n;
for (int i = 0; i < n; ++n)
a += x[i] * coeff;
我肯定会在不到0.7秒的时间内完成。
答案 3 :(得分:1)
您正在使用多项式时间算法(n ** 4
),因此可以说它很慢,特别是对于较大的n
。也许一个复杂度较低的算法可以帮到你?
如果您希望优化代码,可以要求编译器为您进行优化,或者只是用汇编语言编写并完成它。不要试图猜测C ++编译器。
答案 4 :(得分:1)
如何重写代码,以便在没有-O的情况下编译时我可以获得相同的时间(或接近它)?换句话说,如何手动优化嵌套循环?
你在汇编中写下它们。祝你好运 ,BTW。
编译器中优化开关的目的是让你说,“编译器,我希望你尝试生成快速组装。”默认情况下,编译器不进行优化,因为这些优化可能会抑制生成的代码的可调试性。所以你必须特别要求他们。
一般来说,编译器优化代码所做的事情不是你可以手动完成的。有些可以(比如循环展开),但其他人不能。