2 ms·
I'm not completely on board with #4 (initialization of variables). I agree that 'declaring and then assigning a value' _within the same block_ vs 'initialized d
by optymizer1 13y ago
I'm not completely on board with #4 (initialization of variables). I agree that 'declaring and then assigning a value' _within the same block_ vs 'initialized declaration' has no speed gains, only downsides.
However, if the assignment can happen in a _different block_ (maybe inside an 'if' block) you could save 1 memory write, depending on how many times the if condition is satisfied.
This obviously optimizes for speed at the expense of maintainability, and it's for the programmer to make intelligent trade offs, but the fact is that one method is faster than the other.
Silly example that decrements 'a' repeatedly if condition is non-zero:
//temp = 0 inside if block
int dec(int a, int condition) {
int temp;
if (condition) {
temp = 0; //<-- memory write depends on condition
//compute something here in a loop
while (temp < a) {
a -= (temp++);
}
}
return a;
}
//temp = 0 at declaration
int dec2(int a, int condition) {
int temp = 0; // <-- memory write always executed
if (condition) {
//compute something here in a loop
while (temp < a) {
a -= (temp++);
}
}
return a;
}
We can disassemble the output to verify that dec() will only write to memory if the condition was satisfied (see 400458), while dec2() will always write into temp (see 400482):
# <dec>
400448: push %rbp
400449: mov %rsp,%rbp
40044c: mov %edi,0xffec(%rbp)
40044f: mov %esi,0xffe8(%rbp)
400452: cmpl $0x0,0xffe8(%rbp) # if (condition)
400456: je 400473 <dec+0x2b>
400458: movl $0x0,0xfffc(%rbp) # temp = 0 <-- depends on condition
40045f: jmp 40046b <dec+0x23> # while
400461: mov 0xfffc(%rbp),%eax
400464: sub %eax,0xffec(%rbp)
400467: addl $0x1,0xfffc(%rbp)
40046b: mov 0xfffc(%rbp),%eax
40046e: cmp 0xffec(%rbp),%eax
400471: jl 400461 <dec+0x19> # loop
400473: mov 0xffec(%rbp),%eax # return a
400476: leaveq
400477: retq
# <dec2>
400478: push %rbp
400479: mov %rsp,%rbp
40047c: mov %edi,0xffec(%rbp)
40047f: mov %esi,0xffe8(%rbp)
400482: movl $0x0,0xfffc(%rbp) # temp = 0 <-- always executed
400489: cmpl $0x0,0xffe8(%rbp) # if (condition)
40048d: je 4004a3 <dec2+0x2b>
40048f: jmp 40049b <dec2+0x23> # while
400491: mov 0xfffc(%rbp),%eax
400494: sub %eax,0xffec(%rbp)
400497: addl $0x1,0xfffc(%rbp)
40049b: mov 0xfffc(%rbp),%eax
40049e: cmp 0xffec(%rbp),%eax
4004a1: jl 400491 <dec2+0x19> # loop
4004a3: mov 0xffec(%rbp),%eax # return a
4004a6: leaveq
4004a7: retq
The above code was compiled with gcc 4.1.2 on amd64/linux. gcc -O2 and gcc -O3 completely do away with the 'temp' variable and generate fewer instructions.
- ant512 13y agoI'd suggest that, if a variable with an undefined value is a valid state for the program, the declaration is in the wrong place. How about this alternative? int dec(int a, int condition) { if (condition) { int temp = 0; //compute something here in a loop while (temp < a) { a -= (temp++); } } return a; }
- optymizer1 13y agoThat's the obvious next step, but it's not valid C89 and that may or may not be relevant for people doing embedded programming. Personally, I'm all for using newer standards and declaring variables locally when it makes sense. Sometimes I like to see all variables at the top of the function though.
- nitrogen 13y agoI'm not particularly familiar with x86-64 calling conventions. Where is the code that sets up the stack frame? Would this code look different if temp were created inside the if() block, not just initialized there?
- optymizer1 13y agoIn C, variables have function-level storage. I don't recall the actual term from the C standard, but the storage for _all_ variables declared in any blocks inside a function is allocated at the beginning of the function, when the stack frame is set up. So there is 'no way' to create 'temp' only inside the if block.
- nitrogen 13y agoOkay. I knew variables had block-level scope. I've read some of the ISO C standard, but hadn't caught the part where variables have function-level storage. I was wondering because, years ago, I read some disassembled code that did lots of stack allocations in the middle of functions, and not just for the purpose of calling another function. Can you point to a reference showing that variables have function-level storage? The closest I can find in the C standard draft I'm looking at is 6.2.4.6, which suggests block-level lifetimes for variables: "For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way."