我有 C/C++ 代码,如下所示:
static int function(double *I) {
int n = 0;
// more instructions, loops,
for (int i; ...; ++i)
n += fabs(I[i] > tolerance);
return n;
}
function(I); // return value is not used.
编译器内联函数,但是它不会优化
n
操作。
我希望编译器能够识别该值永远不会仅用作 rhs。
是否有一些副作用阻碍了优化?
编译器似乎并不重要,我尝试了Intel和gcc。 积极优化,
-O3
谢谢
更完整的代码(完整代码是此类块的重复):
280 // function registers
281 double q0 = 0.0;
282 double q1 = 0.0;
283 double q2 = 0.0;
284
285 #if defined (__INTEL_COMPILER)
286 #pragma vector aligned
287 #endif // alignment attribute
288 for (int a = 0; a < int(N); ++a) {
289 q0 += Ix(a,1,0)*Iy(a,0,0)*Iz(a,0,0);
290 q1 += Ix(a,0,0)*Iy(a,1,0)*Iz(a,0,0);
291 q2 += Ix(a,0,0)*Iy(a,0,0)*Iz(a,1,0);
292 }
293 #endif // not SSE
294
295 //contraction coefficients
296 qK0 += q0*C[k+0];
297 qK1 += q1*C[k+0];
298 qK2 += q2*C[k+0];
299
300 Ix += 3*dim2d;
301 Iy += 3*dim2d;
302 Iz += 3*dim2d;
303
304 }
305 Ix = Ix - 3*dim2d*K;
306 Iy = Iy - 3*dim2d*K;
307 Iz = Iz - 3*dim2d*K;
308
309 // normalization, scaling, and storage
310 if(normalize) {
311 I[0] = scale*NORMALIZE[1]*NORMALIZE[0]*(qK0 + I[0]);
312 num += (fabs(I[0]) >= tol);
313 I[1] = scale*NORMALIZE[2]*NORMALIZE[0]*(qK1 + I[1]);
314 num += (fabs(I[1]) >= tol);
315 I[2] = scale*NORMALIZE[3]*NORMALIZE[0]*(qK2 + I[2]);
316 num += (fabs(I[2]) >= tol);
317 }
318 else {
319 I[0] = scale*(qK0 + I[0]);
320 num += (fabs(I[0]) >= tol);
321 I[1] = scale*(qK1 + I[1]);
322 num += (fabs(I[1]) >= tol);
323 I[2] = scale*(qK2 + I[2]);
324 num += (fabs(I[2]) >= tol);
325 }
326
327
328 return num;
我唯一的猜测是潜在的浮点异常,这会带来副作用
代码确实使用了
n
,首先将其初始化为 0,然后在函数左侧的循环内使用,可能会产生副作用 (fabs
)。
是否实际使用该函数的return并不重要,
n
本身已被使用。
更新:我在 MSVC10 中尝试了这段代码,它优化了整个功能。给我一个我可以尝试的完整示例。
#include <iostream>
#include <math.h>
const int tolerance=10;
static int function(double *I) {
int n = 0;
// more instructions, loops,
for (int i=0; i<5; ++i)
n += fabs((double)(I[i] > tolerance));
return n;
}
int main()
{
double I[]={1,2,3,4,5};
function(I); // return value is not use
}
我认为这个问题的简短答案是,仅仅因为编译器可以在理论上进行一些优化并不意味着它将会。 没有什么是免费的。 如果编译器要优化掉 n,那么就必须有人编写代码来做到这一点。
对于既是奇怪的极端情况又是微不足道的空间节省的东西来说,这听起来像是大量的工作。 我的意思是,人们多久会编写执行复杂计算的函数却丢弃结果? 在这种情况下是否值得编写复杂的优化来恢复 8 个字节的堆栈空间?
我不能肯定地说它会产生影响,但你可能想研究一下 GCC 的
pure
和 const
属性(http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes .html)。它基本上告诉编译器该函数仅对其输入进行操作并且没有副作用。
根据这些额外信息,它可能能够确定该呼叫是不必要的。
尽管我在其他线程中遇到过一些争论,但所有编译器都是完美的并且不会错过任何优化。 编译器并不完美,并且经常无法捕获优化。
像这样有趣的:
int 乐趣 ( int a ) { 开关(a&3) { 情况 0:返回(a+4); 情况1:返回(a+2); 情况2:返回(a); 情况3:返回(0); } 返回(1); }
在最长的时间里,如果您将返回值留在最后,您会收到一个错误,表明该函数定义了返回类型但未能返回值。 有些编译器会抱怨函数末尾的 return() ,如果没有它也会抱怨。
据我所知,gcc 与 llvm 相比,gcc 在文件内的函数内进行优化,其中 llvm 对其输入的所有内容进行优化。 您可以将整个项目的字节码加入到一个文件中,并一次性优化整个过程。 目前,gcc 输出的性能比 llvm 好百分之十几或更多,这很有趣。 给它时间。
也许在您的情况下,您正在使用两个未声明为静态(const)的输入进行计算,因此结果 n 可能会改变。 如果针对每个功能进行优化,则无法进一步减少它。 所以我的猜测是它正在优化每个函数,并且调用函数不知道什么会影响我在系统上的动态输入,即使未使用返回值,它仍然需要计算函数(I)来解决依赖于的任何内容I. 我认为这不是无限循环,...意味着施加了一些限制? 如果这里不再是动态的而不是静态的,函数(I)可能是一个终止无限循环函数,或者它可能在那里等待中断服务例程来修改 I 并将其踢出无限循环。