Skip to main content

Timer-Prüfung und Zugriffszeiten (Performance)

Du hast drei Möglichkeiten im Kopf:

  1. Referenz-Check: if (timer != null)

  2. Boolean-Flag: if (hasTimer)

  3. Bit-Flag: if ((flags & TIMER_BIT) != 0)

Was kostet was?

  1. Null-Check (timer != null):

    Das ist extrem billig. In modernen CPUs ist der Vergleich gegen 0 (null) eine der schnellsten Operationen überhaupt (1 Taktzyklus).

    • Kosten: Nahezu Null.

    • Problem: Wenn timer ein eigenes Objekt ist (z.B. eine Instanz der Klasse Timer), hast du beim Zugriff danach (timer.isExpired()) einen Pointer Chase. Die CPU muss an eine andere Stelle im RAM springen, um das Objekt zu laden. Das kann einen Cache Miss verursachen, was „teuer“ ist (relativ gesehen).

  2. Boolean (boolean hasTimer):

    In Java verbraucht ein boolean in einer Klasse meistens so viel Platz wie ein byte (manchmal wird es intern auch auf 4 Bytes ausgerichtet, je nach JVM/Padding).

    • Kosten: Gleich schnell wie der Null-Check.

    • Vorteil: Keiner gegenüber dem Null-Check, außer du sparst dir das Timer-Objekt ganz (siehe „Die beste Lösung“).

  3. Bit-Flag:

    Das lohnt sich in Java nur, wenn du Millionen von Objekten hast und Speicherplatz sparen musst.

    • Kosten: Du brauchst Rechenoperationen (Bitmaskierung &), um den Wert zu lesen. Das ist theoretisch „langsamer“ als ein reiner boolean-Lesezugriff, aber in der Praxis vernachlässigbar.

Die wirkliche Bremse: Branch Prediction

Das Teure ist nicht der Vergleich (==), sondern der Sprung (if). Die CPU versucht zu raten, ob der Timer gesetzt ist oder nicht (Branch Prediction).

  • Wenn 99% der Objekte keinen Timer haben, ist die CPU superschnell.

  • Wenn es zufällig 50/50 verteilt ist, verschwendet die CPU Zeit mit falschen Vorhersagen (Branch Misprediction).

Die effizienteste Lösung (Best Practice)

Vergiss das Timer-Objekt und Flags. Nutze einen primitiven long Timestamp direkt im Objekt.

Java

class MeinObjekt {
    // 0 (oder -1) bedeutet: Kein Timer gesetzt.
    // > 0 bedeutet: Das ist der Zeitstempel, wann er abläuft.
    long timerExpiration = 0; 

    public void check() {
        long now = System.currentTimeMillis();
        
        // Ein einziger Vergleich deckt beides ab: 
        // 1. Ist Timer an? (timerExpiration > 0)
        // 2. Ist er abgelaufen? (timerExpiration < now)
        if (timerExpiration > 0 && now > timerExpiration) {
            // Timer feuern!
            timerExpiration = 0; // Reset
        }
    }
}

Warum ist das am schnellsten?

  1. Kein Objekt-Overhead: Du musst kein Timer-Objekt erzeugen oder dereferenzieren.

  2. Cache-Freundlichkeit: Der long-Wert liegt direkt im Speicherbereich von MeinObjekt. Die CPU lädt ihn zusammen mit dem Objekt in den Cache.

  3. Logik: Du prüfst Existenz und Ablauf in einem Rutsch.

Leave a Reply