Home > java > Dead Code Elimination in Java

Dead Code Elimination in Java

21 dicembre 2010

Translate in English with Google Translate
Tempo fa mi sono imbattuto in questa simpatica tecnica di ottimizzazione del codice ad opera del compilatore java. In poche parole il compilatore non si limita semplicemente a compilare il codice che gli viene sottoposto ma in base a degli algoritmi di analisi, cerca di eliminare/ottimizzare il compilato da generare. Le ottimizzazioni effettuate dal compilatore sono di diverso tipo ma in questo articolo ci soffermeremo solo su una di queste.
Nel nostro caso ci soffermeremo sul “Dead Code Elimination” ( DCE ) che rappresenta un algoritmo di ottimizzazione del codice compilato che consente di eliminare quella porzione di codice che non produce alcun risultato utile. L’eliminazione di questa parte di codice, dal compilato, permette di compattare il codice generato, pertanto riduce il LOC ossia “Line Of Code” eliminando codice “morto” ossia codice/variabili che non verrebbe mai eseguito/assegnate in quanto irraggiungibile.

Esempio

Partiamo da un semplice esempio.

Esempio di partenza
Consideriamo una classe con un metodo che preveda una istruzione condizionale, la classica IF CONDITION, come segue:

public class TestClass {
    public void metodo(boolean y) {
        int x = 100;
        if( y )
            x = 90;
    }
}

Il metodo è molto semplice: se la condizione è vera allora alla variabile locale verrà assegnato il valore 90, altrimenti resterà assegnato il valore di inizializzazione 100.
Compiliamola e poi disassembliamola usando il disassemblatore Sun/Oracle javap -c:

$JAVA_HOME/bin/javac TestClass.java  && $JAVA_HOME/bin/javap -c  TestClass
public class TestClass extends java.lang.Object{
public TestClass();
  Code:
   0:	aload_0
   1:	invokespecial	#1; //Method java/lang/Object."":()V
   4:	return

public void metodo(boolean);
  Code:
   0:	bipush	100
   2:	istore_2
   3:	iload_1
   4:	ifeq	10
   7:	bipush	90
   9:	istore_2
   10:	return
}

Fino ad adesso è tutto regolare, la condizione IF valuta la variabile locale boolean passata come parametro per determinare se asssegnare o meno il valore 90 alla variabile locale int.

 

Prima modifica

Modifichiamo la classe e cabliamo nella condizione IF il valore booleano TRUE, in questo modo:

public class TestClass {
    public void metodo(boolean y){
        int x = 100;
        if( true )
            x = 90;
    }
}

Compiliamola e poi disassembliamola come fatto in precedenza:

$JAVA_HOME/bin/javac TestClass.java  && $JAVA_HOME/bin/javap -c  TestClass
public class TestClass extends java.lang.Object{
public TestClass();
  Code:
   0:	aload_0
   1:	invokespecial	#1; //Method java/lang/Object."":()V
   4:	return

public void metodo(boolean);
  Code:
   0:	bipush	100
   2:	istore_2
   3:	bipush	90
   5:	istore_2
   6:	return
}

Adesso notiamo una cosa “strana”…ossia che non c’è più l’istruzione IF !!!
Perchè?
Semplice: era inutile, pertanto il compilatore ha “deciso” di eliminarla in quanto in ogni caso sarebbe stata eseguita l’assegnazione alla variabile locale intera per il valore 90. La condizione non prevedeva scelta in quanto la condizione if(true) è sempre vera pertanto è anche possibile eliminarla senza pregiudicare l’esecuzione. Diverso era il caso precedente in quanto la valorizzazione del parametro del metodo determinava la scelta true|false, pertanto staticamente non si poteva sapere che valore possesse essere passato.

 

Seconda modifica

Facciamo una ulteriore modifica alla nostra classe e “cabliamo” il valore FALSE nella condizione IF, come segue:

public class TestClass {
    public void metodo(boolean y){
        int x = 100;
        if( false )
            x = 90;
    }
}

Compiliamola e poi disassembliamola come fatto in precedenza:

$JAVA_HOME/bin/javac TestClass.java  && $JAVA_HOME/bin/javap -c  TestClass
public class TestClass extends java.lang.Object{
public TestClass();
  Code:
   0:	aload_0
   1:	invokespecial	#1; //Method java/lang/Object."":()V
   4:	return

public void metodo(boolean);
  Code:
   0:	bipush	100
   2:	istore_2
   3:	return
}

Adesso viene eliminata non solo l’istruzione IF ma anche l’assegnazione.
Perchè?
Anche adesso è semplice. Non solo la condizione IF è inutile ma anche l’assegnazione della variabile è inutile in quanto non verrà mai eseguita. Il concetto che sta dietro questa scelta è giusto: perchè allocare spazio per una variabile che non potrà mai essere utilizzata in quanto l’accesso è bloccato? quindi eliminiamola!
Questo è, alla fine, un esempio di “eliminazione di codice morto” mentre il caso precedente era un caso generico di “ottimizzazione”.

 

Errore di compilazione per le istruzioni WHILE e FOR

Diverso è il caso delle istruzioni while e for, in cui il problema viene risolto alla fonte. Nel senso che il compilatore verifica preventivamente l’esito dell’istruzione e rilancia un errore in fase di compilazione.
Iniziamo con l’esempio del ciclo WHILE, come segue:

public class TestClass {
    public void metodo(boolean y){
        int x = 100;
        while( false ){
            x = 90;
        }
    }
}

Se proviamo a compilare la classe otteniamo questo risultato:

$JAVA_HOME/bin/javac TestClass.java
TestClass.java:4: unreachable statement
        while( false ){
                      ^
1 error

Passiamo all’esempio dell’istruzione FOR, come segue:
Lo stesso risultato viene ottenuto anche nel caso in cui utilizziamo l’istruzione FOR, come segue:

public class TestClass {
    public void metodo(boolean y){
        int x = 100;
        for( ;false; ){
            x = 90;
        }
    }
}

Se proviamo a compilare la classe otteniamo questo risultato:

$JAVA_HOME/bin/javac TestClass.java
TestClass.java:4: unreachable statement
        for( ;false; ){
                      ^
1 error
Categorie:java
%d blogger cliccano Mi Piace per questo: