Vermutungen über Laufzeit sind fast immer falsch. Lyx macht das Messen einfach: eine monotone Uhr in der Standardbibliothek, statische Analysen im Compiler, und Binaries ohne Laufzeitsystem, das die Messung verfälscht.
→ Fehlersuche · Attribute & Pragmas · DO-178C
var t0: int64 := GetTimeNs(CLOCK_MONOTONIC);
… Arbeit …
var t1: int64 := GetTimeNs(CLOCK_MONOTONIC);
| Uhr | Wofür |
|---|---|
CLOCK_MONOTONIC | Zeitspannen — springt nicht, wenn die Systemzeit gestellt wird |
CLOCK_MONOTONIC_RAW | dasselbe, ohne NTP-Feinregelung |
CLOCK_PROCESS_CPUTIME_ID | Rechenzeit des Prozesses statt Wanduhr |
CLOCK_THREAD_CPUTIME_ID | Rechenzeit eines Threads |
CLOCK_REALTIME | Zeitpunkte, nicht Zeitspannen |
Für Zeitstempel im Alltag gibt es NowMs() und NowUs() aus std.time; zum Messen ist die monotone Uhr die richtige Wahl.
import std.io;
import std.alloc;
import std.time;
// Zwei Fassungen derselben Aufgabe: Summe eines Feldes
fn SummeEinfach(a: int64, n: int64): int64 {
var s: int64 := 0;
var i: int64 := 0;
while (i < n) { s := s + peek64(a + i * 8); i := i + 1; }
return s;
}
// Vierfach abgerollt — dieselbe Rechnung, weniger Schleifenkopf
fn SummeAbgerollt(a: int64, n: int64): int64 {
var s: int64 := 0;
var i: int64 := 0;
var grenze: int64 := n - (n - (n / 4) * 4);
while (i < grenze) {
s := s + peek64(a + i * 8)
+ peek64(a + (i + 1) * 8)
+ peek64(a + (i + 2) * 8)
+ peek64(a + (i + 3) * 8);
i := i + 4;
}
while (i < n) { s := s + peek64(a + i * 8); i := i + 1; }
return s;
}
fn Messen(name: pchar, wert: int64, ns: int64): void {
PrintLn(StrConcat(StrConcat(name, ": Summe="), StrConcat(IntToStr(wert),
StrConcat(", ", StrConcat(IntToStr(ns / 1000000), " ms")))));
}
fn main(): int64 {
var n: int64 := 2000000;
var a: int64 := allocZeroed(n * 8);
var i: int64 := 0;
while (i < n) { poke64(a + i * 8, i); i := i + 1; }
// Monotone Uhr: sie springt nicht, wenn die Systemzeit gestellt wird
var t0: int64 := GetTimeNs(CLOCK_MONOTONIC);
var s1: int64 := SummeEinfach(a, n);
var t1: int64 := GetTimeNs(CLOCK_MONOTONIC);
var s2: int64 := SummeAbgerollt(a, n);
var t2: int64 := GetTimeNs(CLOCK_MONOTONIC);
Messen("einfach ", s1, t1 - t0);
Messen("abgerollt", s2, t2 - t1);
free(a, n * 8);
return 0;
}
Zwei Läufe hintereinander, gemessen mit lyxc 1.1.3I:
einfach : Summe=1999999000000, 24 ms
abgerollt: Summe=1999999000000, 21 ms
einfach : Summe=1999999000000, 22 ms
abgerollt: Summe=1999999000000, 15 ms
Zwei Dinge sind daran wichtiger als die Zahlen:
lyxc prog.lyx --static-analysis --call-graph -o prog
=== Aufrufgraph ===
Funktionen: 2, Aufrufkanten: 1
keine Rekursion
von main nicht erreichbar: 0
=== Statische Analyse: Stapelbedarf ===
Regel: 0 lokale Variablen, ca. 0 Byte Rahmen
main: 1 lokale Variablen, ca. 8 Byte Rahmen
groesster Rahmen: ca. 8 Byte
–call-graph beantwortet die WCET-Vorfragen: Gibt es Rekursion? Ist etwas unerreichbar (also toter Code im Nachweis)?–static-analysis nennt den Stapelbedarf je Funktion — die Grundlage für @stack_limit(N).–mcdc zeigt, welche Bedingungen strukturell nicht abgedeckt sind.Für sicherheitskritischen Code zählt nicht der Mittelwert, sondern die obere Schranke. Die Sprachmittel dafür:
| Mittel | Zusage |
|---|---|
@wcet(N) | Diese Funktion soll in N Zeiteinheiten fertig sein |
@stack_limit(N) | Ihr Rahmen bleibt unter N Byte; Rekursion ohne diese Angabe ist verboten |
@flight_crit | Kein new/alloc, keine lokalen Felder, FPU-Traps für NaN/Inf |
limit(N) an Schleifen | Obergrenze der Durchläufe steht im Quelltext |
@wcet(N)ist seit lyxc 1.1.4A wieder unauffällig (#1704, nachgemessen):F(20) + 1liefert42.
Bis 1.1.3O verlor der Aufruf einer so annotierten Funktion innerhalb eines Ausdrucks den Rückgabetyp und rechnete alsf64— heraus kam ein Bitmuster. Wer mit einem älteren Compiler arbeitet, weist das Ergebnis erst einer Variablen zu (var v: int64 := F(20);).@stack_limit,@dalund@flight_critwaren nie betroffen.
-O0 zum Gegenprüfen: Verschwindet der Unterschied, lag er an der Faltung, nicht am Code.Und der Hinweis aus dem Bestand: Eine Änderung, die 3 ms bringt, aber den Code unlesbar macht, ist ein schlechtes Geschäft — es sei denn, die 3 ms stehen in einer Anforderung.
Die Reihenfolge, in der sich das Suchen lohnt:
alloc im Innersten einer Schleife ist ein Kandidat (Speicher in der Praxis).
Letzte Aktualisierung: 2026-08-20 — Messungen mit lyxc 1.1.3I durchgeführt; der dabei gefundene @wcet-Defekt #1704 ist mit lyxc 1.1.4A nachgemessen behoben.