Ich habe Rust-Code gehabt, der unglaublich schnell war. Bei der weiteren Analyse stellte sich heraus: Der Compiler hat das Ergebnis schon zur Compile-Zeit bestimmt.
Im Rahmen meiner Project-Euler-Lösungen habe ich den folgenden Code in Rust geschrieben:
fn sum_of_natural_numbers(end: i64, step: i64) -> i64 {
let count = end / step + 1;
count * (count - 1) * step / 2
}
fn solution_closed_form() -> i64 {
sum_of_natural_numbers(999, 3) + sum_of_natural_numbers(999, 5)
- sum_of_natural_numbers(999, 15)
}
Das ganze habe ich mit Criterion auf meinem Laptop ausgemessen, es läuft in 1,3 ns durch. Bei einer Taktrate von 4,2 GHz sind das etwas über 5 CPU-Zyklen. Das ist absurd schnell.
Da wollte ich dann einmal wissen, was der Rust-Compiler da für Maschinencode erzeugt hat. Das geht mit Cargo erstaunlich einfach:
cargo install cargo-asm
cargo asm --lib project_euler_rust::solutions::solution_0001::solution_closed_form
Und das hier kam dabei heraus:
.section .text.project_euler_rust::solutions::solution_0001::solution_closed_form,"ax",@progbits
.p2align 4
.type project_euler_rust::solutions::solution_0001::solution_closed_form,@function
project_euler_rust::solutions::solution_0001::solution_closed_form:
.cfi_startproc
mov eax, 233168
ret
Also alles, was die Funktion macht, ist die Lösung (233.168) in das 32-Bit Register eax zu schreiben. Damit ist sie sofort fertig. Der Compiler hat also diese Berechnung schon ausgeführt und das fertige Ergebnis ins Programm gepackt.
Es ist schon interessant zu sehen, wie krass heutige Compiler sind. Das, was man an Code schreibt, hat recht wenig mit dem Maschinencode zu tun. Und das ist auch gut so, weil es dadurch viel schneller wird.