Ich habe gegenüber einem Kollegen behauptet, dass er if (i < input.size() - 1) print(0);
in dieser Schleife optimiert würde, damit er input.size()
nicht in jeder Iteration gelesen wird, aber es stellt sich heraus, dass dies nicht der Fall ist!
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);
}
}
Laut dem Compiler Explorer mit gcc-Optionen -O3 -fno-exceptions
lesen wir tatsächlich input.size()
jede Iteration und verwenden sie lea
, um eine Subtraktion durchzuführen!
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
Interessanterweise findet diese Optimierung in Rust statt. Es sieht so aus, als würde es i
durch eine Variable ersetzt j
, die bei jeder Iteration dekrementiert wird, und der Test i < input.size() - 1
wird durch so etwas ersetzt 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);
}
}
}
Im Compiler-Explorer sieht die entsprechende Assembly wie folgt aus:
cmpq %r12, %rbx
jae .LBB0_4
Ich habe nachgesehen und bin mir ziemlich sicher, dass es der Zähler r12
ist xs.len() - 1
und rbx
ist. Früher gibt es ein add
für rbx
und ein mov
außerhalb der Schleife in r12
.
Warum ist das? Es scheint, als ob GCC in der Lage ist, das zu integrieren, size()
und operator[]
wie es getan hat, sollte es wissen können, dass sich size()
dies nicht ändert. Aber vielleicht urteilt der Optimierer von GCC, dass es sich nicht lohnt, ihn in eine Variable zu ziehen? Oder vielleicht gibt es eine andere mögliche Nebenwirkung, die dies unsicher machen würde - weiß jemand Bescheid?
cout.operator<<()
. Der Compiler weiß nicht, dass diese Black-Box-Funktion keinen Verweis auf die std::vector
von einem globalen erhält .
println
oder der operator<<
Schlüssel ist.
println
ist wahrscheinlich eine komplexe Methode, der Compiler kann Probleme haben zu beweisen, dassprintln
der Vektor nicht mutiert.