views:

301

answers:

4

hi.

I have C/C++ code, that looks like this:

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.

compiler inlines function, however it does not optimize out n manipulations. I would expect compiler is able to recognize that value is never used as rhs only. Is there some side effect, which prevents optimization?

compiler does not seem to matter, I tried Intel and gcc. Aggressive optimization, -O3

Thanks

fuller code (full code is repetition of such blocks):

  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;

my only guess is potentially floating-point exceptions, which introduced side effects

+6  A: 

The code does use n, first when it initializes it to 0 and then inside the loop on the left hand side of a function with possible side effects (fabs).

Whether or not you actually use the return of the function is irrelevant, n itself is used.

Update: I tried this code in MSVC10 and it optimized the whole function away. Give me a full example I could try.

#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
}
Blindy
Quick and to the point answer. Beat to the punch here :)
Michael Dorgan
yes, but n is only used with respect to itself, it is not used as a right-hand value in any of expressions. I would expect compiler to recognize that
aaa
After inlining, the code looks like `int n = 0; for(...) n+=...;`. `n` is not used anymore after that loop. It could produce the possible side effects of `fabs` without using storage for `n`, if i'm not missing something. It could even know about `fabs` (see `-fno-builtin`), and optimize out the entire loop. But apparently it doesn't do that.
Johannes Schaub - litb
To elaborate: Even though it's inlined, the compiler has to generate just one version of `function`, and if it generated a version that didn't return anything, you would never actually be able to call it when you DID need the return value.
Mark B
@Potato: Maybe, maybe not. You can't really assume that.
Blindy
@Mark B: The OP appears to be inspecting the inlined code, not the version that was generated with a body. Why `n` was not removed from inlined code is indeed not clear. Apparently, the compiler was not smart enough. Also, compilers will not normally generate a standalone body for an inlined function if the function does not explicitly *require* a body (like called through a pointer, for example)
AndreyT
@Johannes: The way I see it, `n` is used in the return, you just choose to ignore the value in this case. You may use it in the future or not, the compiler doesn't know (or it wouldn't if `static` wasn't used). I agree that an insanely aggressive optimizer could remove this, but it seems like it's too much work for gcc's current implementation.
Blindy
GCC is optimizing like mad. This sounds like a simple job for it.
Johannes Schaub - litb
@Blindy: The compiler *does* know that the return value is not used when the function call is *inlined*, as OP explicitly stated.
AndreyT
@I could try to isolate full function, but the actual function is rather complex part of library. may take me some time to do so
aaa
I've seen that kind of very agresssive optimizations in VS2008 / 2010, even across compilation units. It's damn impressive! Makes it a little bit harder to write "appropriate" performance test routines, though :)
peterchen
+2  A: 

I think the short answer to this question is, just because a compiler can make some optimization in theory doesn't mean that it will. Nothing comes for free. If the compiler is going to optimize away n, then someone has to write the code to do it.

That sounds like a lot of work for something that is both a bizarre corner case and a trivial space savings. I mean, how often do people write functions that perform complex calculations only to discard the result? Is it worth writing complex optimizations to recover 8 bytes worth of stack space in such cases?

Peter Ruderman
Given the recently posted code, this is probably the correct answer. Compilers aren't magic machines.
Torlack
In the general case, it isn't that uncommon. A function might generate two return values, only one of which you actually care about. Or the actual work of the function could be completely orthogonal to the return value; a function that builds an array, for instance, and returns the number of values in the array. If you don't care about the number of values in the array, so long as the array got built, then you might ignore the return value outright.
Dennis Zickefoose
I agree that it's quite routine to ignore return values. But in such cases, the return value is usually trivial, as in your array example. That means you'd get a small savings by optimizing it away. The code to do that optimization, however, would be complex.
Peter Ruderman
@peter I thought unnecessary code path will be presented as parse tree not connected to anything, which compiler just drops.it seems like a pretty common scenario
aaa
I wouldn't think so, but without being privy to the internal workings of the compilers you used, I can't comment further.
Peter Ruderman
Often is isn't a question of the return value not being used, but detecting if the act of calling the function generates side effects that must be maintained.
Torlack
+1  A: 

I can't say for certain that it will have an effect, but you may want to look into GCC's pure and const attribute (http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html). It basically tells the compiler that the function only operates on its input and has no side effects.

Given this extra information, it may be able to determine that the call is unnecessary.

Evan Teran
they seem to have added more optimization options which may be useful to me.thanks
aaa
I would assume you want to put a const on the input function(const double *I) both for correctness and perhaps it helps the compiler optimize, dont know till you try.
dwelch
@dweltch: while you are correct in giving that advice. I was actually referring to a GCC **attribute** named `const`. See the link I posted for details.
Evan Teran
A: 
dwelch