0Pricing
Java Academy · Lezione

Evitare l'eliminazione del codice morto

Blackhole e stato

Evitare l'eliminazione del codice morto è una lezione Java Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Java Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Java Academy include 4 lezioni in totale.

Evitare la Dead-Code Elimination

La principale trappola dei microbenchmark è la dead-code elimination (DCE): se il JIT dimostra che un risultato non viene mai utilizzato, elimina il calcolo. Il benchmark misura quindi il nulla. JMH offre due strumenti per impedirlo: restituire i valori e utilizzare Blackhole.

La trappola della DCE

Questo ciclo calcola una radice quadrata un milione di volte, ma non utilizza mai il risultato. Un compilatore intelligente può eliminare l'intero ciclo.

public class Main {
    public static void main(String[] args) {
        long t = System.nanoTime();
        for (int i = 0; i < 1_000_000; i++) {
            double ignored = Math.sqrt(i); // result thrown away
        }
        System.out.println("ns: " + (System.nanoTime() - t));
    }
}

Correzione 1: restituire il risultato

La difesa più semplice consiste nel restituire il valore calcolato dal metodo @Benchmark. JMH utilizza ogni valore restituito, quindi la JVM non può dimostrare che sia inutilizzato.

import org.openjdk.jmh.annotations.Benchmark;

public class Bench {
    @Benchmark
    public double sqrt() {
        return Math.sqrt(42.0); // returned -> consumed by JMH
    }
}

Correzione 2: Blackhole

Quando un benchmark produce più valori o non è possibile restituirne uno, inietti un Blackhole e chiami consume(...). Blackhole convince il JIT che ogni valore viene utilizzato, senza il costo di un'elaborazione reale.

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.infra.Blackhole;

public class Bench {
    @Benchmark
    public void manyValues(Blackhole bh) {
        for (int i = 0; i < 100; i++) {
            bh.consume(Math.sqrt(i));
        }
    }
}

Oggetti @State

Una classe @State contiene input opachi per l'ottimizzatore, impedendo il constant folding. JMH la istanzia e la passa al benchmark. L'ambito può essere Thread, Benchmark (condiviso) o Group.

import org.openjdk.jmh.annotations.*;

public class Bench {
    @State(Scope.Thread)
    public static class Data {
        public int x = 21;
    }

    @Benchmark
    public int multiply(Data d) {
        return d.x * 2; // d.x is not a compile-time constant
    }
}

@Setup e @TearDown

All'interno di una classe @State, i metodi annotati con @Setup vengono eseguiti prima della misurazione, mentre quelli annotati con @TearDown vengono eseguiti dopo. Level controlla la frequenza: Trial (una volta), Iteration o Invocation.

import org.openjdk.jmh.annotations.*;

@State(Scope.Thread)
public class Data {
    int[] arr;
    @Setup(Level.Trial)
    public void init() {
        arr = new int[1000];
        for (int i = 0; i < arr.length; i++) arr[i] = i;
    }
}

Evitare gli input costanti

Non passate mai costanti letterali al metodo sottoposto a test. Math.sqrt(2.0) può essere valutato già come costante, mentre Math.sqrt(state.value) no. Recuperate sempre gli input da un campo @State.

Restituire più valori

Se il benchmark produce naturalmente due risultati, potete restituirne uno e consumare l'altro tramite un Blackhole, oppure combinarli. Lasciare uno dei due non consumato riapre la falla della DCE.

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.infra.Blackhole;

public class Bench {
    @Benchmark
    public int twoResults(Blackhole bh) {
        int a = 3 * 7;
        int b = 5 + 9;
        bh.consume(b);
        return a;
    }
}

Overhead di Blackhole

Blackhole è progettato per essere estremamente economico e a sua volta resistente alle ottimizzazioni, ma non è completamente gratuito. Per operazioni ultrarapide, preferite restituire un singolo valore; riservate Blackhole ai cicli e ai casi con più valori.

Verificare la plausibilità dei risultati

Se un benchmark riporta un tempo impossibilmente basso (inferiore al nanosecondo per un lavoro reale), sospettate la DCE o il constant folding. Ricontrollate che i risultati vengano restituiti o consumati e che gli input provengano da @State.

Mettere tutto insieme

Un benchmark robusto: leggete gli input da un oggetto @State, eseguite il lavoro e poi restituite il risultato con return oppure consumatelo con Blackhole.consume. Con queste tre abitudini, la DCE e il constant folding non possono alterare le vostre misurazioni.

Verifica rapida

Verificate la vostra comprensione di come impedire l'eliminazione del codice morto.

Riepilogo

Avete imparato a mantenere accurati i benchmark:

  • Eliminazione del codice morto elimina i calcoli inutilizzati.
  • Restituite un singolo risultato, così JMH lo consuma.
  • Usate Blackhole.consume per i cicli e per più valori.
  • Gli oggetti @State mantengono gli input opachi, impedendo il constant folding.
  • @Setup / @TearDown preparano e ripuliscono lo stato a un Level scelto.

Domande Frequenti

La lezione «Evitare l'eliminazione del codice morto» è gratuita?

Sì — il testo completo di «Evitare l'eliminazione del codice morto» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Java Academy, passa a CoddyKit PRO. Il corso Java Academy include 4 lezioni in totale.

Cosa imparerò in «Evitare l'eliminazione del codice morto»?

Blackhole e stato Eserciti Java Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Java Academy?

Non è richiesta alcuna esperienza precedente. Java Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Evitare l'eliminazione del codice morto»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Java Academy?

Sì. Ogni lezione Java Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Perché usare JMH
  2. Scrivere un benchmark
  3. Riscaldamento e iterazioni
  4. Evitare l'eliminazione del codice morto
← Torna a Java Academy