First of all, ind
is so close to int
that you typoed it in the question, so it's a bad choice of variable name. Using i
as a loop index is a near-universal convention.
Any decent compiler will do lifetime analysis on the int i
that's in scope for the whole function and see that the i=0
at the start disconnects it from its previous value. Uses of i
after that are unrelated to uses before that, because of the unconditional assignment that didn't depend on anything computed from the previous value.
So from an optimizing compiler's perspective, there shouldn't be a difference. Any difference in the actual asm output should be considered a missed-optimization bug in whichever one is worse.
In practice, gcc 5.3 -O3 -march=haswell
targeting x86-64 makes identical loops for narrow scope vs. function scope in a simple test I did. I had to use three arrays inside the loop to get gcc to use indexed addressing modes instead of incrementing pointers, which is good because one-register addressing modes are more efficient on Intel SnB-family CPUs.
It reuses the same register for i
in both loops, instead of saving/restoring another call-preserved register (e.g. r15
). Thus, we can see this potential worry about more variables in a function leading to worse register allocation is not in fact a problem. gcc does a pretty good job most of the time.
These are the two functions I tested on godbolt (see the link above). They both compile to identical asm with gcc 5.3 -O3
.
#include <unistd.h>
// int dup(int) is a function that the compiler won't have a built-in for
// it's convenient for looking at code with function calls.
void single_var_call(int *restrict dst, const int *restrict srcA,
const int *restrict srcB, int a) {
int i;
for(i=0; i < a; i++) { dst[i] = dup(srcA[i] + srcB[i]); }
for(i=0; i < a; i++) { dst[i] = dup(srcA[i]) + srcB[i]+2; }
}
// Even with restrict, gcc doesn't fuse these loops together and skip the first store
// I guess it can't because the called function could have a reference to dst and look at it
void smaller_scopes_call(int *restrict dst, const int *restrict srcA,
const int *restrict srcB, int a) {
for(int i=0; i < a; i++) { dst[i] = dup(srcA[i] + srcB[i]); }
for(int i=0; i < a; i++) { dst[i] = dup(srcA[i]) + srcB[i]+2; }
}
For correctness / readability reasons: prefer for (int i=...)
The C++ / C99 style of limiting the scope of loop variables has advantages for humans working on the code. You can see right away that the loop counter isn't used outside the loop. (So can the compiler).
It's a good way to prevent errors like initializing the wrong variable.