Tuesday, March 5, 2013

Storing Function parameters in Registers instead of Stack

Generally whenever we call a function, the function parameters will store in a stack (in RAM). During the execution of that function, lot of push and pop operations done on that stack which eventually slow down the execution of that function. To increase the speed of execution we can store function parameters in cpu registers (ecx, edx).

In GNU C, you declare certain things about functions called in your program which help the compiler optimize function calls and check your code more carefully. The keyword __attribute__allows you to specify special attributes when making a declaration. This keyword is followed by an attribute specification inside double parentheses.

For example, please see the following code, where I am storing ADD function variables X and Y in CPU registers instead of stack using __attribute__.


// FASTCALL.c

#include <stdio.h >

__attribute__((fastcall,noinline)) int ADD (int X, int Y)
{
  return X + Y;
}

int main () {
  int Z = ADD (10, 20);
  printf("\n Z = %d \n", Z );
}
 Command to Execute

$gcc FASTCALL.c
$./a.out

To see whether these variable X, Y are stored in registers are not, type the following command in command line to generate assembly code of FASTCALL.c It create a file FASTCALL.s which contains the assembly code of FASTCALL.c.

Command to generate assembler Code

 
 $gcc -O2 -S -c FASTCALL.c

The content of  FASTCALL.s is as follows.

        .file    "FASTCALL.c"
    .text
    .p2align 4,,15
.globl ADD
    .type    ADD, @function
ADD:
    pushl    %ebp
    movl    %esp, %ebp
    leal    (%edx,%ecx), %eax
    popl    %ebp
    ret
    .size    ADD, .-ADD
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string    "\n Z = %d \n"
    .text
    .p2align 4,,15
.globl main
    .type    main, @function
main:
    pushl    %ebp
    movl    $20, %edx
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $16, %esp
    movl    $10, %ecx
    call    ADD
    movl    $.LC0, 4(%esp)
    movl    $1, (%esp)
    movl    %eax, 8(%esp)
    call    __printf_chk
    leave
    ret
    .size    main, .-main
    .ident    "GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

In the above assembler code, you can find that X and Y are stored in edx, ecx registers instead of esp. If you want to check the time difference without and with __attrribute_, you can use clock_t in your code.

3 comments:

  1. can u show the same program with clock_t

    ReplyDelete
    Replies
    1. WITH FASTCALL:

      #include
      #include
      #include

      __attribute__((fastcall,noinline)) int ADD (int X, int Y)
      {
      return X + Y;
      }

      int main () {
      clock_t start;
      int Z = ADD (10, 20);
      printf("\n Z = %d \n", Z );
      clock_t end;
      unsigned long millis = (end - start)/10000;
      printf("\n Time taken for execution = %ld \n", millis );
      }

      WITH OUT FASTCALL:

      #include
      #include
      #include

      int ADD (int X, int Y)
      {
      return X + Y;
      }

      int main () {
      clock_t start;
      int Z = ADD (10, 20);
      printf("\n Z = %d \n", Z );
      clock_t end;
      unsigned long millis = (end - start)/10000;
      printf("\n Time taken for execution = %ld \n", millis );
      }

      Execute above 2 call and find the difference :-)

      Delete
    2. blogger is not displaying header files...include stdio.h unistd.h sys/times.h files

      Delete