Why initialized variable before usage is faster than uninitialized variable [closed] - c

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 8 years ago.
Improve this question
For example, variable i in the C code below:
t03a.c:
#include <stdio.h>
int main(void)
{
int a=0;
int b=1;
int i=0;//When a variable is declared,the variable is initialised
for(i=0;i<1000000000;++i);
return 0;
}
t03b.c:
#include <stdio.h>
int main(void)
{
int a=0;
int b=1;
int i; // not initialized
for(i=0;i<1000000000;++i);
return 0;
}
The result (test by linux time):
t03a:
real 0m0.527s
user 0m0.250s
sys 0m0.004s
t03b:
real 0m2.499s
user 0m2.431s
sys 0m0.003s
Of course, I ran the test many times. Why is t03a faster than t03b?

I have run the test for a couple of times. Below is a pretty average result of both programs:
[xxx#arch-desktop: ~/]$ time ./t03a
real 0m2.819s
user 0m2.817s
sys 0m0.000s
[xxx#arch-desktop: ~/]$ time ./t03b
real 0m2.815s
user 0m2.813s
sys 0m0.000s
Compiled with gcc -std=c99 -o t03a t03a.c. Maybe you can try using the optimizing parameter(i.e. -O3).
I don't think there should be any difference if it's compiled in the right way. Declaring and defining it directly shouldn't be any different from defining it later.

On my machine (an Intel x86 with gcc), both routines compile to almost identical assembly. The only difference is that 0 is put on the stack twice in the second example. I can't imagine this taking much time.
Compiled with -O1 I get identical (if not especially useful) code for both examples.

Initialize is a useful practice for the style of the code and for avoiding some null and logic errors.
So this is the main scope of initialization.
But for my tests the difference of performance for t03a.c and t03b.c on Linux architecture have not significant difference.
Be carefully to analyse results because also temperature of CPU, mainboard and ect ...influence the time of compilation (you need to the take an relative and absolute error if you want to demonstrate your assertion when the difference is so little).

In my case the assembly code from t03a.c is the same than t03b.c, but with one more instruction, so I guess you were doing something else and the scheduler gave another process a little more execution time or maybe it was for another reason, however te execution time aren't so great and the difference is not much to conclude something, the difference is only one instruction (you have to think that you are doing 1000000000+ instructions in less than 3 seconds, so 1 instruction won't make a difference that will be perceived).
.file "t03b.c"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $0, -8(%rbp)
movl $1, -12(%rbp)
movl $0, -4(%rbp)
movl $0, -4(%rbp)
jmp .L2
.L3:
addl $1, -4(%rbp)
.L2:
cmpl $999999999, -4(%rbp)
jle .L3
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.8.3 20140911 (Red Hat 4.8.3-7)"
.section .note.GNU-stack,"",#progbits
.file "t03b.c"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $0, -8(%rbp)
movl $1, -12(%rbp)
movl $0, -4(%rbp)
jmp .L2
.L3:
addl $1, -4(%rbp)
.L2:
cmpl $999999999, -4(%rbp)
jle .L3
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.8.3 20140911 (Red Hat 4.8.3-7)"
.section .note.GNU-stack,"",#progbits

Related

How does x=x+1 is evaluated by the compiler and how is represented in assembly?

I'm trying to understand how does the compiler "sees" the i+1 part from expression i=i+1. I understand that i=3 means putting the value 3 in the location memory of variable i.
My guess about the i=i+1 is that the compiler expects a value from the right side of the "=" operator, so it gets the value from the location memory of variable i (which is 3, after the assignment) and add 1 to it, and the final result of the "i+1" expression(3+1=4) is stored back into the location memory of variable i, as a value. Is that correct?
And if it is, it means that any variable/combination of variables and literals present on the right side of an "=" operator will always be replaced with the value stored in them and those value can be added/substracted/etc with the values from other variables/literals (as in the x+1 expression), whilst the final result of those calculations will also be literal values (ex: 5, literal strings, etc), and will also be stored like values in a single variable on the left side of the "=" operator.
I'm also curious how this code is seen in assembly, and what are the main operations of this incrementation of i ( i = i+1);
#include <stdio.h>
int main()
{
int i = 3;
i = i + 1; // i should have the value of 4 stored back in it;
return 0;
}
This is not answerable for the general case. It depends on the target platform. If you want to inspect the assembly, you can do so with the -S parameter with gcc. When I did that to your code, it gave me this:
/tmp$ cat main.s
.file "main.c"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $3, -4(%rbp)
addl $1, -4(%rbp)
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Debian 9.2.1-8) 9.2.1 20190909"
.section .note.GNU-stack,"",#progbits
A brief little explanation of what is happening here. First we push the value of the stackpointer. This is so that we can jump back later.
.cfi_startproc
pushq %rbp
Then we set up the stack frame with this code. It corresponds to declaring variables.
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
Then we have this. Comments are mine.
movl $3, -4(%rbp) # i = 3;
addl $1, -4(%rbp) # i = i + 1;
Lastly, we return from the main function
movl $0, %eax # Store 0 in the "return register"
popq %rbp # Restore stackpointer
.cfi_def_cfa 7, 8
ret # return
Note that there is not a 1-1 relationship between lines. Not even for very simple lines.
Please also note that C imposes requirement on the observable behavior of the program and not on the generated assembly. So for instance, a compiler might remove the whole body for the main function because the variable i is not used in an observable way. And it will if you use optimization. When I recompiled your code with -O3 I got this instead:
/tmp/$ cat main.s
.file "main.c"
.text
.section .text.startup,"ax",#progbits
.p2align 4
.globl main
.type main, #function
main:
.LFB11:
.cfi_startproc
xorl %eax, %eax
ret
.cfi_endproc
.LFE11:
.size main, .-main
.ident "GCC: (Debian 9.2.1-8) 9.2.1 20190909"
.section .note.GNU-stack,"",#progbits
Notice how much that got removed from main. It can be interesting that movl $0, %eax has changed to xorl %eax, %eax. If you think about it, it's pretty obvious that this is a "set zero" operation. One could reasonably argue why anyone would write stuff like that. Well, the optimizer does certainly not optimize for readability. There are a few reasons why it is better. You can read about them here: What is the best way to set a register to zero in x86 assembly: xor, mov or and?

Why does a simple C "hello world" program not work if gcc -O3 without "volatile"?

I have the following C program
int main() {
char string[] = "Hello, world.\r\n";
__asm__ volatile ("syscall;" :: "a" (1), "D" (0), "S" ((unsigned long) string), "d" (sizeof(string) - 1)); }
which I want to run under Linux with with x86 64 bit. I call the syscall for "write" with 0 as fd argument because this is stdout.
If I compile under gcc with -O3, it does not work. A look into the assembly code
.file "test_for_o3.c"
.text
.section .text.startup,"ax",#progbits
.p2align 4,,15
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
subq $40, %rsp
.cfi_def_cfa_offset 48
xorl %edi, %edi
movl $15, %edx
movq %fs:40, %rax
movq %rax, 24(%rsp)
xorl %eax, %eax
movq %rsp, %rsi
movl $1, %eax
#APP
# 5 "test_for_o3.c" 1
syscall;
# 0 "" 2
#NO_APP
movq 24(%rsp), %rcx
xorq %fs:40, %rcx
jne .L5
xorl %eax, %eax
addq $40, %rsp
.cfi_remember_state
.cfi_def_cfa_offset 8
ret
.L5:
.cfi_restore_state
call __stack_chk_fail#PLT
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0"
.section .note.GNU-stack,"",#progbits
tells us that gcc has simply not put the string data into the assembly code. Instead, if I declare "string" as "volatile", it works fine.
However, the idea of "volatile" is just to use it for variables that can change their values by (from the view of the executing function) unexpected events, isn't it? "volatile" can make code much slower, hence it should be avoided if possible.
As I would suppose, gcc must assume that the content of "string" must not be ignored because the pointer "string" is used as an input parameter in the inline assembly (and gcc has no idea what the inline assembly code will do with it).
If this is "allowed" behaviour of gcc, where can I read more about all the formal constraints I have to be aware of when writing code for -O3?
A second question would be what the "volatile" statement along with the inline assembly directive does exactly. I just got used to mark all inline assembly directives with "volatile" because it had not worked otherwise, in some situations.

Testing C code by compiling with -s switch? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I want to test my code (I know my code is still incomplete -- yes I am planning to complete it before I compile it) to see if it gives the correct assembly code by compiling with -s switch, how do I do this?
I am not very familiar with compiling. All I did so far was save my file. Now I need to compile it to be able to run it.
typedef enum {MODE_A, MODE_B, MODE_C, MODE_D, MODE_E} mode_t;
long switch3 (long *p1, long *p2, mode_t action) {
long result = 0;
switch(action){
case MODE_A:
case MODE_B:
case MODE_C:
case MODE_D:
case MODE_E:
default:; // don't forget the colon
}
return result;
}
Open an editor, Vi or Emacs for example
Type and save your code in a file, maybe main.c
Exit the editor
Type gcc -S main.c or clang -S main.c in the terminal. You can also add a -fverbose-asm flag to tell the complier to add more information in the output, or a -masm=intel flag to inspect the assembly output much nicer.
On success, a file named main.s will be generated under the current directory, containing the assembly code; on failure, error messages will be printed on the screen.
Also note that your C code will only be compiled when it's compilable, so you have to modify your code first. At least, change default; to default:;
Here is the assembly code produced by clang -S main.c on my machine:
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 11
.globl _switch3
.align 4, 0x90
_switch3: ## #switch3
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movl %edx, -20(%rbp)
movq $0, -32(%rbp)
movl -20(%rbp), %edx
subl $4, %edx
movl %edx, -36(%rbp) ## 4-byte Spill
ja LBB0_2
jmp LBB0_1
LBB0_1:
jmp LBB0_2
LBB0_2:
jmp LBB0_3
LBB0_3:
movq -32(%rbp), %rax
popq %rbp
retq
.cfi_endproc
.subsections_via_symbols
To compile without linking using GNU Compiler Collection (gcc) you can use the -S switch:
jan#jsn-dev:~/src/so> gcc -S main.c
main.c: In function ‘switch3’:
main.c:11:12: error: expected ‘:’ before ‘;’ token
default;
^
After correcting your code with the suggested fix, you get:
jan#jsn-dev:~/src/so> gcc -S main.c
jan#jsn-dev:~/src/so> cat main.s
.file "main.c"
.text
.globl switch3
.type switch3, #function
switch3:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -24(%rbp)
movq %rsi, -32(%rbp)
movl %edx, -36(%rbp)
movq $0, -8(%rbp)
movq -8(%rbp), %rax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size switch3, .-switch3
.ident "GCC: (SUSE Linux) 4.8.3 20140627 [gcc-4_8-branch revision 212064]"
.section .note.GNU-stack,"",#progbits

Understanding an unexpected result due to an unmatched prototype (C89)

I have a program goo.c
void foo(double);
#include <stdio.h>
void foo(int x){
printf ("in foo.c:: x= %d\n",x);
}
which is called by foo.c
int main(){
double x=3.0;
foo(x);
}
I compile and run
gcc foo.c goo.c
./a.out
Guess what? I get "x= 1" as result. Then I find the signature of 'foo' should have been void foo(int). Apparently, my double input value 3.0 has to be downcast to an int. But, if I try to see the value of (int) 3.0 with the test program:
int main(){
double x=3.0;
printf ("%d", ((int) x));
}
I get 3 as output, which makes the earlier ` x= 1' even more hard to understand. Any idea? For information, my gcc is run with ANSI C standard. Thanks.
[EDIT] If I use gcc -S as suggested by JS1,
I get goo.s
.file "goo.c"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movabsq $4613937818241073152, %rax
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, -24(%rbp)
movsd -24(%rbp), %xmm0
call foo
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",#progbits
and foo.s
.file "foo.c"
.section .rodata
.LC0:
.string "in foo.c:: x= %d\n"
.text
.globl foo
.type foo, #function
foo:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",#progbits
Anyone who knows how to read Assembly can help figure out the source problem?
Understanding why you get '1' requires a bit of ASM and x86-64 ABI
knowledge. First of all, goo.c and foo.c are two separate compilation
units. The only thing that foo.c knows about the foo function is
the bogus prototype.
The bogus prototype is as follows: void foo(double);. It's a function
that takes only a single double argument. The x86-64 ABI mandates that
the doubles are passed through the xmm registers (The exact phrasing
is 'If the class is SSE, the next available vector register is used,
the registers are taken in the order from %xmm0 to %xmm7.'.
That means that when the compiler sets up the arguments to call the
foo() function, it's going to pass the argument via %xmm0. In
simplified asm what happens is:
mov 3.0, %xmm0
call foo
Now, foo(), on it's side, believes it's going to recieve an int. The
x86-64 ABI says: 'If the class is INTEGER, the next available register
of the sequence %rdi, %rsi, %rdx, %rcx, %r8 and %r9 is used.'. The first
argument is supposed to be passed via %rdi. That means that foo()
will do something like:
mov %rdi, %rsi
mov 0xabcd, %rdi // 0xabcd being the address of the "%d" string
call printf
So you're going to end up printing whatever was in %rsi, and not %xmm0.
But why 1? You'll get an idea by issuing the following commands:
./a.out a
./a.out a b
./a.out a b c
See a pattern? Let's go back to the simplified assembly:
main:
mov 3.0, %xmm0
call foo
ret
foo:
mov %rdi, %rsi
mov 0xabcd, %rdi // 0xabcd being the address of the "%d" string
call printf
ret
As you can see, nothing is setting %rdi until it reaches foo(),
where it's passed on to printf. Which means 1 was passed to main
in the first place. Now, in the question, main is given the following
prototype: int main(). But the compiler actually setup the function to
have the following prototype instead: int main (int argc, char *argv[],
char *envp[]). The first argument, thus stored in %rdi, is actually
argc. That's why the program was printing 1.

Incrementing a variable through embedded assembly language

I am trying to understand how to embed assembly language in C (using gcc on x86_64 architecture). I wrote this program to increment the value of a single variable. But I am getting garbage value as output. And ideas why?
#include <stdio.h>
int main(void) {
int x;
x = 4;
asm("incl %0": "=r"(x): "r0"(x));
printf("%d", x);
return 0;
}
Thanks
Update The program is giving expected result on gcc 4.8.3 but not on gcc 4.6.3. I am pasting the assembly output of the non-working code:
.file "abc.c"
.section .rodata
.LC0:
.string "%d"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
pushq %rbx
subq $24, %rsp
movl $4, -20(%rbp)
movl -20(%rbp), %eax
incl %edx
movl %edx, %ebx
.cfi_offset 3, -24
movl %ebx, -20(%rbp)
movl $.LC0, %eax
movl -20(%rbp), %edx
movl %edx, %esi
movq %rax, %rdi
movl $0, %eax
call printf
movl $0, %eax
addq $24, %rsp
popq %rbx
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",#progbits
You don't need to say x twice; once is sufficient:
asm("incl %0": "+r"(x));
The +r says that the value will be input and output.
Your way, with separate inputs and output registers, requires that you take the input from %1, add one, and write the output to %0, but you can't do that with incl.
The reason it works on some compilers is because GCC is free to allocate both %0 and %1 to the same register, and appears to have done so in those cases, but it does not have to. Incidentally, if you want to prevent GCC allocating an input and output to the same register (say, if you want to initialize the output before using the input to calculate a final output), you need to use the & modifier.
The documentation for the modifiers is here.

Resources