Comment

Comment by optymizer1

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.
parent optymizer1 13 years ago

Discussion

Live from the official Hacker News API

A modern reader for Hacker News · SvelteKit + Skeleton