#ifdef #ifndef in Java


106

Dubito che ci sia un modo per creare condizioni in fase di compilazione in Java come #ifdef #ifndef in C ++.

Il mio problema è che ho un algoritmo scritto in Java e ho tempi di esecuzione diversi per migliorare l'algoritmo. Quindi voglio misurare quanto tempo risparmio quando viene utilizzato ogni miglioramento.

In questo momento ho una serie di variabili booleane che vengono utilizzate per decidere durante il tempo di esecuzione quale miglioramento deve essere utilizzato e quale no. Ma anche il test di queste variabili influenza il tempo di esecuzione totale.

Quindi voglio trovare un modo per decidere durante il tempo di compilazione quali parti del programma devono essere compilate e utilizzate.

Qualcuno sa come farlo in Java. O forse qualcuno sa che non esiste un modo del genere (sarebbe anche utile).

Risposte:


126
private static final boolean enableFast = false;

// ...
if (enableFast) {
  // This is removed at compile time
}

I condizionali come quello mostrato sopra vengono valutati in fase di compilazione. Se invece usi this

private static final boolean enableFast = "true".equals(System.getProperty("fast"));

Quindi qualsiasi condizione dipendente da enableFast verrà valutata dal compilatore JIT. Il sovraccarico per questo è trascurabile.


Questa soluzione è migliore della mia. Quando ho provato a inizializzare le variabili con un valore esterno preimpostato, il tempo di esecuzione è tornato a 3 secondi. Ma quando ho definito le variabili come variabili di classe statica (e non una variabile locale di funzione) il tempo di esecuzione è tornato a 1 secondo. Grazie per l'aiuto.
jutky

6
IIRC, funzionava anche prima che Java avesse un compilatore JIT. Il codice è stato rimosso da javaccredo. Funzionava solo se l'espressione per (diciamo) enableFastera un'espressione costante del tempo di compilazione.
Stephen C

2
Sì, ma questo condizionale deve risiedere all'interno di un metodo, giusto? Che dire del caso in cui abbiamo un mucchio di stringhe finali statiche private che vorremmo impostare. (ad es. un insieme di URL del server impostati in modo diverso per la produzione e la gestione
temporanea

3
@tomwhipple: vero, in più questo non ti permette di fare qualcosa come: private void foo(#ifdef DEBUG DebugClass obj #else ReleaseClass obj #endif )
Zonko

3
che dire delle importazioni (ad esempio, riguardo a classpath)?
n611x007

44

javac non produrrà codice compilato che è irraggiungibile. Usa una variabile finale impostata su un valore costante per il tuo #definee ifun'istruzione normale per il #ifdef.

È possibile utilizzare javap per dimostrare che il codice non raggiungibile non è incluso nel file della classe di output. Ad esempio, considera il codice seguente:

public class Test
{
   private static final boolean debug = false;

   public static void main(String[] args)
   {
       if (debug) 
       {
           System.out.println("debug was enabled");
       }
       else
       {
           System.out.println("debug was not enabled");
       }
   }
}

javap -c Test fornisce il seguente output, indicando che solo uno dei due percorsi è stato compilato (e l'istruzione if non lo era):

public static void main(java.lang.String[]);
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #3; //String debug was not enabled
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return

2
Questo javac è specifico o questo comportamento è effettivamente garantito da JLS?
Pacerier

@pacerier, non ho idea se questo sia garantito da JLS, ma è stato vero per ogni compilatore java che ho incontrato dagli anni '90, con la possibile eccezione di prima della 1.1.7, e solo perché non l'ho fatto provalo allora.

12

Penso di aver trovato la soluzione, è molto più semplice.
Se definisco le variabili booleane con il modificatore "finale" il compilatore Java stesso risolve il problema. Perché sa in anticipo quale sarebbe il risultato del test di questa condizione. Ad esempio questo codice:

    boolean flag1 = true;
    boolean flag2 = false;
    int j=0;
    for(int i=0;i<1000000000;i++){
        if(flag1)
            if(flag2)
                j++;
            else
                j++;
        else
            if(flag2)
                j++;
            else
                j++;
    }

esegue circa 3 secondi sul mio computer.
E questo

    final boolean flag1 = true;
    final boolean flag2 = false;
    int j=0;
    for(int i=0;i<1000000000;i++){
        if(flag1)
            if(flag2)
                j++;
            else
                j++;
        else
            if(flag2)
                j++;
            else
                j++;
    }

dura circa 1 secondo. Lo stesso tempo che impiega questo codice

    int j=0;
    for(int i=0;i<1000000000;i++){
        j++;
    }

1
È interessante. Sembra che JIT supporti già la compilazione condizionale! Funziona se quelle finali sono in un'altra classe o in un altro pacchetto?
joeytwiddle

Grande! Quindi credo che questa debba essere un'ottimizzazione del runtime, il codice non viene effettivamente rimosso in fase di compilazione. Va bene finché utilizzi una VM matura.
joeytwiddle,

@joeytwiddle, la parola chiave è "finché utilizzi" una VM matura.
Pacerier

2

Non l'ho mai usato, ma esiste

JCPP è un'implementazione Java completa, conforme, autonoma e pura del preprocessore C. È inteso per essere utile alle persone che scrivono compilatori in stile C in Java utilizzando strumenti come sablecc, antlr, JLex, CUP e così via. Questo progetto è stato utilizzato per preelaborare con successo gran parte del codice sorgente della libreria GNU C. A partire dalla versione 1.2.5, può anche preelaborare la libreria Objective C di Apple.

http://www.anarres.org/projects/jcpp/


1
Non sono sicuro che questo soddisfi le mie necessità. Il mio codice è scritto in Java. Forse mi stai proponendo di ottenere i loro sorgenti e di usarli per preelaborare il mio codice?
jutky

2

Se hai davvero bisogno della compilazione condizionale e usi Ant , potresti essere in grado di filtrare il tuo codice e fare una ricerca e sostituzione in esso.

Ad esempio: http://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html

Allo stesso modo è possibile, ad esempio, scrivere un filtro per sostituirlo LOG.debug(...);con /*LOG.debug(...);*/. Ciò sarebbe comunque eseguito più velocemente del if (LOG.isDebugEnabled()) { ... }materiale, per non parlare di essere più conciso allo stesso tempo.

Se usi Maven , c'è una funzione simile descritta qui .


2

Manifold fornisce un preprocessore Java completamente integrato (nessuna procedura di compilazione o sorgente generata). Si rivolge esclusivamente alla compilazione condizionale e utilizza direttive in stile C.

Java Preprocessor di Manifold


1

Utilizzare il Factory Pattern per passare tra le implementazioni di una classe?

Il tempo di creazione dell'oggetto non può essere una preoccupazione ora, vero? Quando viene calcolata la media su un lungo periodo di tempo, la componente più grande del tempo speso dovrebbe essere ora nell'algoritmo principale, no?

A rigor di termini, non hai davvero bisogno di un preprocessore per fare ciò che cerchi di ottenere. Ci sono probabilmente altri modi per soddisfare le tue esigenze oltre a quello che ho proposto ovviamente.


Le modifiche sono molto minori. Come testare alcune condizioni per conoscere in anticipo il risultato richiesto invece di ricalcolarlo. Quindi il sovraccarico della chiamata alla funzione potrebbe non essere adatto a me.
jutky

0
final static int appFlags = context.getApplicationInfo().flags;
final static boolean isDebug = (appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.