#ifdef #ifndef в Java


106

Я сомневаюсь, что есть способ создать условия времени компиляции в Java, такие как #ifdef #ifndef в C ++.

Моя проблема в том, что у меня есть алгоритм, написанный на Java, и у меня другое время работы улучшается для этого алгоритма. Поэтому я хочу измерить, сколько времени я экономлю при использовании каждого улучшения.

Прямо сейчас у меня есть набор логических переменных, которые используются для решения во время работы, какие улучшения следует использовать, а какие нет. Но даже тестирование этих переменных влияет на общее время работы.

Итак, я хочу найти способ решить во время компиляции, какие части программы следует компилировать и использовать.

Кто-нибудь знает, как это сделать на Java. А может кто знает, что такого способа нет (тоже было бы полезно).

Ответы:


126
private static final boolean enableFast = false;

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

Условные выражения, подобные показанным выше, оцениваются во время компиляции. Если вместо этого вы используете это

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

Затем любые условия, зависящие от enableFast, будут оцениваться JIT-компилятором. Накладные расходы для этого незначительны.


Это решение лучше моего. Когда я попытался инициализировать переменные заданным внешним значением, время работы вернулось к 3 секундам. Но когда я определил переменные как статические переменные класса (а не как локальную переменную функции), время работы вернулось к 1 секунде. Спасибо за помощь.
jutky

6
IIRC, это работало даже до того, как в Java появился JIT-компилятор. Код удалил javacя думаю. Это работало, только если выражение для (скажем) enableFastбыло выражением константы времени компиляции.
Stephen C

2
Да, но это условие должно находиться в методе, верно? А как насчет случая, когда у нас есть набор частных статических final Strings, которые мы хотели бы установить. (например, набор URL-адресов серверов, которые устанавливаются по-разному для производства и подготовки)
tomwhipple

3
@tomwhipple: правда, плюс это не позволяет делать что-то вроде: private void foo(#ifdef DEBUG DebugClass obj #else ReleaseClass obj #endif )
Zonko

3
как насчет импорта (например, относительно пути к классам)?
n611x007

44

javac не будет выводить скомпилированный код, который недоступен. Используйте конечную переменную, для которой задано постоянное значение для вашего, #defineи обычный ifоператор для #ifdef.

Вы можете использовать javap, чтобы доказать, что недостижимый код не включен в выходной файл класса. Например, рассмотрим следующий код:

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 дает следующий результат, указывающий, что только один из двух путей был скомпилирован (а оператор if - нет):

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
Это конкретный javac или такое поведение действительно гарантируется JLS?
Pacerier

@pacerier, я понятия не имею, гарантировано ли это JLS, но это было верно для каждого компилятора java, с которым я сталкивался с 90-х годов, за возможным исключением до 1.1.7, и только потому, что я не тогда проверьте это.

12

Думаю, что нашел решение, все намного проще.
Если я определю логические переменные с помощью модификатора final, компилятор Java решит проблему. Потому что он заранее знает, каков будет результат проверки этого условия. Например этот код:

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

работает около 3 секунд на моем компьютере.
И этот

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

работает около 1 секунды. В то же время этот код занимает

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

1
Это интересно. Похоже, JIT уже поддерживает условную компиляцию! Это работает, если эти финалы проходят в другом классе или в другом пакете?
joeytwiddle

Большой! Тогда я считаю, что это должна быть оптимизация времени выполнения, код фактически не удаляется во время компиляции. Это нормально, если вы используете зрелую виртуальную машину.
joeytwiddle

@joeytwiddle, Ключевое слово - "пока вы используете" зрелую ВМ.
Pacerier

2

Никогда не использовал, но он существует

JCPP - это полная, совместимая, автономная реализация препроцессора C. на чистом языке Java. Он предназначен для использования людьми, пишущими компиляторы в стиле C на Java с использованием таких инструментов, как sablecc, antlr, JLex, CUP и т. Д. Этот проект был использован для успешной предварительной обработки большей части исходного кода библиотеки GNU C. Начиная с версии 1.2.5, он также может предварительно обрабатывать библиотеку Apple Objective C.

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


1
Я не уверен, что это соответствует моим потребностям. Мой код написан на Java. Может быть, вы предлагаете мне получить их исходники и использовать их для предварительной обработки моего кода?
jutky

2

Если вам действительно нужна условная компиляция и вы используете Ant , вы можете фильтровать свой код и выполнять в нем поиск и замену.

Например: http://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html

Таким же образом вы можете, например, написать фильтр для замены LOG.debug(...);с /*LOG.debug(...);*/. Это все равно будет выполняться быстрее, чем if (LOG.isDebugEnabled()) { ... }все остальное, не говоря уже о том, чтобы быть в то же время более кратким.

Если вы используете Maven , есть подобная функция описана здесь .


2

Manifold предоставляет полностью интегрированный препроцессор Java (без шагов сборки или сгенерированного исходного кода). Он предназначен исключительно для условной компиляции и использует директивы в стиле C.

Препроцессор Java от Manifold


1

Использовать фабричный шаблон для переключения между реализациями класса?

Время создания объекта не может быть проблемой сейчас, не так ли? При усреднении за длительный период времени самая большая часть затраченного времени должна быть теперь в основном алгоритме, не так ли?

Строго говоря, вам не нужен препроцессор, чтобы делать то, чего вы хотите достичь. Скорее всего, есть другие способы удовлетворить ваши требования, чем тот, который я, конечно, предложил.


Изменения очень незначительные. Это похоже на тестирование некоторых условий, чтобы заранее знать запрошенный результат вместо его повторного вычисления. Так что накладные расходы на вызов функции могут мне не подходить.
jutky

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.