I build a similar example code with different optimizations and below are the results. After the first example I made some important changes to the code - I changed all global variables to local ones with as small scope as possible and the code size dropped significantly. The most important change is that the variables are no longer allocated to RAM, but to registers. This makes the code much more efficient.
Code:
#include <avr/io.h> // Include register definitions
int mask = 0b11110000;
int fourbits;
int main(void)
{
// Infinite loop
while(1)
{
DDRB = 0b00000000;
PORTB = 0b01010101;
fourbits = 0b1000;
PORTB = PORTB & mask;
PORTB = PORTB | fourbits;
}
}
No optimization:
Program Memory Usage : 310 bytes
Data Memory Usage : 4 bytes
Optimize -O1:
Program Memory Usage : 258 bytes
Data Memory Usage : 4 bytes
Optimize More -O2:
Program Memory Usage : 254 bytes
Data Memory Usage : 4 bytes
Optimize Most -O3:
Program Memory Usage : 254 bytes
Data Memory Usage : 4 bytes
Optimize Size -Os
Program Memory Usage : 254 bytes
Data Memory Usage : 4 bytes
Change global variables to local.
Code:
#include <avr/io.h> // Include register definitions
int main(void)
{
// Infinite loop
while(1)
{
int mask = 0b11110000;
int fourbits;
DDRB = 0b00000000;
PORTB = 0b01010101;
fourbits = 0b1000;
PORTB = PORTB & mask;
PORTB = PORTB | fourbits;
}
}
No optimization:
Program Memory Usage : 304 bytes
Data Memory Usage : 0 bytes
Optimize Most -O3:
Program Memory Usage : 228 bytes
Data Memory Usage : 0 bytes
Conclusion:
One "instruction" in C program can be very complex or very simple. Sometimes a whole line of C code can be useless and is optimized away by the compiler. Different compilers produce different (assembly) code. Different optimization levels produce different code. If you change one line of code, it can affect the produced assembly code dramatically somewhere else. So, there are really no one answer to "how long does one line of code take to execute". If you really want to know, you need to look at the produced assembly code.. or "disassembly".
EDIT: Here are the two disassemblies
Global variables, -O3:
Code:
00000068 LDS R21,0x0100 Load direct from data space
PORTB = 0b01010101;
0000006A LDI R20,0x55 Load immediate
fourbits = 0b1000;
0000006B LDI R24,0x08 Load immediate
0000006C LDI R25,0x00 Load immediate
DDRB = 0b00000000;
0000006D OUT 0x17,R1 Out to I/O location
PORTB = 0b01010101;
0000006E OUT 0x18,R20 Out to I/O location
fourbits = 0b1000;
0000006F STS 0x0103,R25 Store direct to data space
00000071 STS 0x0102,R24 Store direct to data space
PORTB = PORTB & mask;
00000073 IN R18,0x18 In from I/O location
00000074 AND R18,R21 Logical AND
00000075 OUT 0x18,R18 Out to I/O location
PORTB = PORTB | fourbits;
00000076 IN R18,0x18 In from I/O location
00000077 LDS R19,0x0102 Load direct from data space
00000079 OR R18,R19 Logical OR
0000007A OUT 0x18,R18 Out to I/O location
0000007B RJMP PC-0x000E Relative jump
Local variables, -O3:
Code:
int mask = 0b11110000;
int fourbits;
DDRB = 0b00000000;
PORTB = 0b01010101;
00000068 LDI R25,0x55 Load immediate
DDRB = 0b00000000;
00000069 OUT 0x17,R1 Out to I/O location
PORTB = 0b01010101;
0000006A OUT 0x18,R25 Out to I/O location
PORTB = PORTB & mask;
0000006B IN R24,0x18 In from I/O location
0000006C ANDI R24,0xF0 Logical AND with immediate
0000006D OUT 0x18,R24 Out to I/O location
PORTB = PORTB | fourbits;
0000006E SBI 0x18,3 Set bit in I/O register
0000006F RJMP PC-0x0006 Relative jump