Does anyone have any advice on articles about C compiler optimizations in general (especially gcc)? I'm doing my first serious C work in ten years, and I keep wondering if I should fuss with things like this or let the compiler handle it all: foo->bar->baz[i].oof = foo->bar->baz[i].durb + meep; vs what *tmp = foo->bar->baz[i]; tmp->oof = tmp->durb + meep; EDIT: I'm not asking for a link to this: https://gcc.gnu.org/o…
However, if you're ever in doubt, I recommend compiling very short functions and viewing their output.
typedef struct {
int oof;
int durb;
} baz_t;
typedef struct {
baz_t *baz;
} bar_t;
typedef struct {
bar_t *bar;
} foo_t;
void f(foo_t *foo, int i, int meep) {
foo->bar->baz[i].oof = foo->bar->baz[i].durb + meep;
}
$ gcc -O2 -c -o test.o test.c
$ objdump -d -r -M intel test.o
test.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 :
0: 48 8b 07 mov rax,QWORD PTR [rdi]
3: 48 63 f6 movsxd rsi,esi
6: 48 8b 08 mov rcx,QWORD PTR [rax]
9: 48 8d 34 f1 lea rsi,[rcx+rsi*8]
d: 03 56 04 add edx,DWORD PTR [rsi+0x4]
10: 89 16 mov DWORD PTR [rsi],edx
12: c3
You can see here that it followed the chain of pointers only once.The one thing to watch out for though is things that gcc isn't allowed to optimize because of C. For example, if a pointer escapes the function (to another function that the optimizer can't see), gcc cannot assume that the pointed-to memory remains unchanged, even if the called function takes a const pointer! Because the function could always cast away const. For example, this variant will have to follow the chain twice:
int g(const foo_t *foo, int i);
void f(foo_t *foo, int i) {
int x = g(foo, foo->bar->baz[i].durb);
foo->bar->baz[i].oof = x;
}
Generally people always use at least -O2. The main difference between -O2 and -O3 is that -O3 is more aggressive with unrolling and other optimizations that increase code size, so sometimes -O2 is faster because of icache pressure. I generally use -O3 on my tightest loops and -O2 (or even -Os) on everything else.