tl; dr: la versione locale mantiene N in un registro, la versione globale no. Dichiarate le costanti con const e sarà più veloce, indipendentemente da come lo dichiarate.
Ecco il codice di esempio che ho usato:
#include <iostream>
#include <math.h>
void first(){
int x=1;
int N = 10000;
for(int i = 0; i < N; ++i)
tan(tan(tan(tan(tan(tan(tan(tan(x++))))))));
std::cout << x;
}
int N=10000;
void second(){
int x=1;
for(int i = 0; i < N; ++i)
tan(tan(tan(tan(tan(tan(tan(tan(x++))))))));
std::cout << x;
}
int main(){
first();
second();
}
(chiamato test.cpp
).
Per osservare il codice assembler generato ho eseguito g++ -S test.cpp
.
ho ottenuto un file enorme, ma con un po 'intelligente ricerca (ho cercato per tan), ho trovato quello che volevo:
dalla funzione first
:
Ltmp2:
movl $1, -4(%rbp)
movl $10000, -8(%rbp) ; N is here !!!
movl $0, -12(%rbp) ;initial value of i is here
jmp LBB1_2 ;goto the 'for' code logic
LBB1_1: ;the loop is this segment
movl -4(%rbp), %eax
cvtsi2sd %eax, %xmm0
movl -4(%rbp), %eax
addl $1, %eax
movl %eax, -4(%rbp)
callq _tan
callq _tan
callq _tan
callq _tan
callq _tan
callq _tan
callq _tan
movl -12(%rbp), %eax
addl $1, %eax
movl %eax, -12(%rbp)
LBB1_2:
movl -12(%rbp), %eax ;value of n kept in register
movl -8(%rbp), %ecx
cmpl %ecx, %eax ;comparing N and i here
jl LBB1_1 ;if less, then go into loop code
movl -4(%rbp), %eax
seconda funzione:
Ltmp13:
movl $1, -4(%rbp) ;i
movl $0, -8(%rbp)
jmp LBB5_2
LBB5_1: ;loop is here
movl -4(%rbp), %eax
cvtsi2sd %eax, %xmm0
movl -4(%rbp), %eax
addl $1, %eax
movl %eax, -4(%rbp)
callq _tan
callq _tan
callq _tan
callq _tan
callq _tan
callq _tan
callq _tan
movl -8(%rbp), %eax
addl $1, %eax
movl %eax, -8(%rbp)
LBB5_2:
movl _N(%rip), %eax ;loading N from globals at every iteration, instead of keeping it in a register
movl -8(%rbp), %ecx
Quindi dal codice dell'assemblatore è possibile vedere (o meno) che, nella versione locale, N viene tenuto in un registro durante l'intero calcolo, mentre nella versione globale, N è riletto dal globale ad ogni iterazione.
Immagino che il motivo principale per cui questo accade sia per cose come il threading, il compilatore non può essere sicuro che N non sia modificato.
se si aggiunge una const
alla dichiarazione di N (const int N=10000
), sarà ancora più veloce rispetto alla versione locale però:
movl -8(%rbp), %eax
addl $1, %eax
movl %eax, -8(%rbp)
LBB5_2:
movl -8(%rbp), %eax
cmpl $9999, %eax ;9999 used instead of 10000 for some reason I do not know
jle LBB5_1
N è sostituita da una costante.
Suppongo che il compilatore possa sospettare che 'x' venga modificato all'interno della funzione' tan' che impedisce molta ottimizzazione. Non sono sicuro che questo avvenga qui. – Pubby
@Pubby L'unica differenza è tuttavia la decelerazione N? –
bene, se 'x' è locale quindi ovviamente non può essere modificato all'interno di' tan'. Quindi le dichiarazioni contano. – Pubby