J'ai revendiqué à un collègue qui if (i < input.size() - 1) print(0);serait optimisé dans cette boucle afin que ce input.size()ne soit pas lu à chaque itération, mais il s'avère que ce n'est pas le cas!
void print(int x) {
std::cout << x << std::endl;
}
void print_list(const std::vector<int>& input) {
int i = 0;
for (size_t i = 0; i < input.size(); i++) {
print(input[i]);
if (i < input.size() - 1) print(0);
}
}
Selon l' explorateur du compilateur avec les options gcc, -O3 -fno-exceptionsnous lisons actuellement input.size()chaque itération et utilisons leapour effectuer une soustraction!
movq 0(%rbp), %rdx
movq 8(%rbp), %rax
subq %rdx, %rax
sarq $2, %rax
leaq -1(%rax), %rcx
cmpq %rbx, %rcx
ja .L35
addq $1, %rbx
Fait intéressant, dans Rust, cette optimisation se produit. Il semble que isoit remplacé par une variable jqui est décrémentée à chaque itération, et le test i < input.size() - 1est remplacé par quelque chose comme j > 0.
fn print(x: i32) {
println!("{}", x);
}
pub fn print_list(xs: &Vec<i32>) {
for (i, x) in xs.iter().enumerate() {
print(*x);
if i < xs.len() - 1 {
print(0);
}
}
}
Dans l' explorateur du compilateur, l'assembly correspondant ressemble à ceci:
cmpq %r12, %rbx
jae .LBB0_4
J'ai vérifié et je suis sûr que r12c'est xs.len() - 1et rbxc'est le comptoir. Plus tôt, il y a un addpour rbxet un movextérieur dans la boucle r12.
Pourquoi est-ce? Il semble que si GCC est en mesure d'aligner le size()et operator[]comme il l'a fait, il devrait pouvoir savoir que size()cela ne change pas. Mais peut-être que l'optimiseur de GCC juge qu'il ne vaut pas la peine de le retirer dans une variable? Ou peut-être y a-t-il un autre effet secondaire possible qui rendrait cela dangereux - quelqu'un le sait-il?
cout.operator<<(). Le compilateur ne sait pas que cette fonction de boîte noire n'obtient pas de référence à std::vectorpartir d'un global.
printlnou operator<<est la clé.
printlnprobablement une méthode complexe, le compilateur peut avoir du mal à prouver queprintlncela ne mute pas le vecteur.