2 ms·
Here's my advice. Generally speaking, the compiler is really smart. I would characterize the optimization you put as a third-grade optimization: GCC is in col
by haberman 12y ago
Here's my advice. Generally speaking, the compiler is really smart. I would characterize the optimization you put as a third-grade optimization: GCC is in college (Clang too, for that matter). It's many steps ahead of that level.
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 <f>:
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.