#ifdef #ifndef en Java

106

Je doute qu'il existe un moyen de créer des conditions de compilation en Java comme #ifdef #ifndef en C ++.

Mon problème est que j'ai un algorithme écrit en Java, et j'ai différentes améliorations du temps d'exécution de cet algorithme. Je veux donc mesurer le temps que je gagne lorsque chaque amélioration est utilisée.

En ce moment, j'ai un ensemble de variables booléennes qui sont utilisées pour décider pendant le temps d'exécution quelle amélioration doit être utilisée et laquelle non. Mais même le test de ces variables influence la durée totale de fonctionnement.

Je veux donc trouver un moyen de décider pendant la compilation quelles parties du programme doivent être compilées et utilisées.

Est-ce que quelqu'un connaît un moyen de le faire en Java. Ou peut-être que quelqu'un sait qu'il n'y a pas de tel moyen (ce serait également utile).

jutky
la source

Réponses:

126
private static final boolean enableFast = false;

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

Les conditions telles que celles présentées ci-dessus sont évaluées au moment de la compilation. Si à la place vous utilisez ceci

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

Ensuite, toutes les conditions dépendant de enableFast seront évaluées par le compilateur JIT. Les frais généraux sont négligeables.

Mark Thornton
la source
Cette solution est meilleure que la mienne. Lorsque j'ai essayé d'initialiser les variables avec une valeur externe prédéfinie, le temps de fonctionnement est revenu à 3 secondes. Mais lorsque j'ai défini les variables comme des variables de classe statiques (et non comme une variable locale de fonction), le temps d'exécution est revenu à 1 seconde. Merci pour l'aide.
jutky
6
IIRC, cela fonctionnait même avant que Java n'ait un compilateur JIT. Le code a été supprimé par javacje pense. Cela ne fonctionnait que si l'expression pour (par exemple) enableFastétait une expression constante de compilation.
Stephen C
2
Oui, mais ce conditionnel doit résider dans une méthode, n'est-ce pas? Qu'en est-il du cas où nous avons un tas de chaînes finales statiques privées que nous aimerions définir. (par exemple, un ensemble d'URL de serveur qui sont définies différemment pour la production et la mise en scène)
tomwhipple
3
@tomwhipple: true, en plus cela ne vous permet pas de faire quelque chose comme: private void foo(#ifdef DEBUG DebugClass obj #else ReleaseClass obj #endif )
Zonko
3
qu'en est-il des importations (par exemple, en ce qui concerne le classpath)?
n611x007
44

javac ne produira pas de code compilé inaccessible. Utilisez une variable finale définie sur une valeur constante pour votre #defineet une ifinstruction normale pour le #ifdef.

Vous pouvez utiliser javap pour prouver que le code inaccessible n'est pas inclus dans le fichier de classe de sortie. Par exemple, considérez le code suivant:

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 donne la sortie suivante, indiquant que seul l'un des deux chemins a été compilé dans (et l'instruction if ne l'était pas):

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
Phil Ross
la source
2
Ce javac est-il spécifique, ou ce comportement est-il réellement garanti par le JLS?
Pacerier
@pacerier, je n'ai aucune idée si cela est garanti par le JLS, mais c'est vrai pour tous les compilateurs java que j'ai rencontrés depuis les années 90, à l'exception possible de la version antérieure à 1.1.7, et uniquement parce que je ne l'ai pas fait testez-le ensuite.
12

Je pense que j'ai trouvé la solution, c'est beaucoup plus simple.
Si je définis les variables booléennes avec le modificateur "final", le compilateur Java lui-même résout le problème. Parce qu'il sait à l'avance quel serait le résultat du test de cette condition. Par exemple ce code:

    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++;
    }

s'exécute environ 3 secondes sur mon ordinateur.
Et celui-là

    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++;
    }

s'exécute environ 1 seconde. Le même temps que ce code prend

    int j=0;
    for(int i=0;i<1000000000;i++){
        j++;
    }
jutky
la source
1
C'est intéressant. Il semble que JIT prend déjà en charge la compilation conditionnelle! Est-ce que ça marche si ces finales sont dans une autre classe ou un autre package?
joeytwiddle
Génial! Ensuite, je pense que cela doit être une optimisation d'exécution, le code n'est pas réellement dépouillé au moment de la compilation. C'est parfait tant que vous utilisez une VM mature.
joeytwiddle
@joeytwiddle, Keyword est "tant que vous utilisez" une VM mature.
Pacerier
2

Je ne l'ai jamais utilisé, mais cela existe

JCPP est une implémentation complète, conforme, autonome et pure Java du préprocesseur C. Il est destiné à être utile aux personnes qui écrivent des compilateurs de style C en Java en utilisant des outils tels que sablecc, antlr, JLex, CUP, etc. Ce projet a été utilisé pour prétraiter avec succès une grande partie du code source de la bibliothèque GNU C. À partir de la version 1.2.5, il peut également prétraiter la bibliothèque Apple Objective C.

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

À M
la source
1
Je ne suis pas sûr que cela réponde à mes besoins. Mon code est écrit en Java. Peut-être me proposez-vous d'obtenir leurs sources et de les utiliser pour prétraiter mon code?
jutky
2

Si vous avez vraiment besoin d'une compilation conditionnelle et que vous utilisez Ant , vous pourrez peut-être filtrer votre code et y effectuer une recherche et un remplacement.

Par exemple: http://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html

De la même manière , vous pouvez, par exemple, écrire un filtre pour remplacer LOG.debug(...);avec /*LOG.debug(...);*/. Cela s'exécuterait toujours plus rapidement que les if (LOG.isDebugEnabled()) { ... }trucs, sans parler d'être plus concis en même temps.

Si vous utilisez Maven , une fonctionnalité similaire est décrite ici .

rustyx
la source
2

Manifold fournit un préprocesseur Java entièrement intégré (aucune étape de construction ni source générée). Il cible exclusivement la compilation conditionnelle et utilise des directives de style C.

Préprocesseur Java de Manifold

Scott
la source
1

Utiliser le modèle d'usine pour basculer entre les implémentations d'une classe?

Le temps de création de l'objet ne peut pas être un problème maintenant, n'est-ce pas? Lorsqu'elle est calculée en moyenne sur une longue période de temps, la plus grande composante du temps passé devrait être dans l'algorithme principal maintenant, n'est-ce pas?

À proprement parler, vous n'avez pas vraiment besoin d'un préprocesseur pour faire ce que vous cherchez à réaliser. Il y a très probablement d'autres moyens de répondre à vos besoins que celui que j'ai proposé bien sûr.

jldupont
la source
Les changements sont très mineurs. Comme tester certaines conditions pour connaître à l'avance le résultat demandé au lieu de le recalculer. Donc, la surcharge de l'appel à la fonction pourrait ne pas me convenir.
jutky
0
final static int appFlags = context.getApplicationInfo().flags;
final static boolean isDebug = (appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
alicanbatur
la source