#ifdef #ifndef em Java

106

Duvido que haja uma maneira de criar condições de tempo de compilação em Java como #ifdef #ifndef em C ++.

Meu problema é que tenho um algoritmo escrito em Java, e tenho diferentes melhorias de tempo de execução para esse algoritmo. Portanto, quero medir quanto tempo economizo quando cada melhoria é usada.

No momento eu tenho um conjunto de variáveis ​​booleanas que são usadas para decidir durante o tempo de execução quais melhorias devem ser usadas e quais não. Mas mesmo o teste dessas variáveis ​​influencia o tempo total de execução.

Portanto, quero descobrir uma maneira de decidir durante o tempo de compilação quais partes do programa devem ser compiladas e usadas.

Alguém conhece uma maneira de fazer isso em Java. Ou talvez alguém saiba que não existe tal caminho (também seria útil).

saliente
fonte

Respostas:

126
private static final boolean enableFast = false;

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

Condicionais como o mostrado acima são avaliados em tempo de compilação. Se ao invés você usar isto

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

Então, quaisquer condições dependentes de enableFast serão avaliadas pelo compilador JIT. A sobrecarga para isso é insignificante.

Mark Thornton
fonte
Esta solução é melhor que a minha. Quando tentei inicializar as variáveis ​​com um valor externo predefinido, o tempo de execução voltou para 3 segundos. Mas quando defini as variáveis ​​como variáveis ​​de classe estáticas (e não uma variável local de função), o tempo de execução voltou a 1 segundo. Obrigado pela ajuda.
jutky
6
IIRC, isso funcionou mesmo antes de o Java ter um compilador JIT. O código foi removido por javaceu acho. Isso funcionava apenas se a expressão para (digamos) enableFastfosse uma expressão de constante de tempo de compilação.
Stephen C
2
Sim, mas esta condicional deve residir em um método, correto? E o caso em que temos um monte de Strings finais estáticas privadas que gostaríamos de definir. (por exemplo, um conjunto de URLs de servidor que são configurados de forma diferente para produção versus teste)
tomwhipple
3
@tomwhipple: true, além disso não permite que você faça algo como: private void foo(#ifdef DEBUG DebugClass obj #else ReleaseClass obj #endif )
Zonko
3
e as importações (por exemplo, em relação ao classpath)?
n611x007
44

javac não produzirá código compilado que esteja inacessível. Use uma variável final definida com um valor constante para seu #definee uma ifdeclaração normal para o #ifdef.

Você pode usar o javap para provar que o código inacessível não está incluído no arquivo de classe de saída. Por exemplo, considere o seguinte código:

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 fornece a seguinte saída, indicando que apenas um dos dois caminhos foi compilado (e a instrução if não foi):

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
fonte
2
Este javac é específico ou é realmente garantido pelo JLS?
Pacerier
@pacerier, não tenho ideia se isso é garantido pelo JLS, mas tem sido verdade para todos os compiladores java que encontrei desde os anos 90, com a possível exceção de anteriores a 1.1.7, e apenas porque eu não teste-o então.
12

Acho que encontrei a solução, é muito mais simples.
Se eu definir as variáveis ​​booleanas com o modificador "final", o próprio compilador Java resolve o problema. Porque sabe de antemão qual seria o resultado do teste dessa condição. Por exemplo, este código:

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

corre cerca de 3 segundos no meu computador.
E este

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

corre cerca de 1 segundo. O mesmo tempo que este código leva

    int j=0;
    for(int i=0;i<1000000000;i++){
        j++;
    }
saliente
fonte
1
Isso é interessante. Parece que o JIT já oferece suporte à compilação condicional! Funciona se essas finais forem em outra classe ou outro pacote?
joeytwiddle de
Ótimo! Então eu acredito que isso deve ser uma otimização de tempo de execução, o código não está realmente sendo removido em tempo de compilação. Tudo bem, desde que você use uma VM madura.
joeytwiddle de
@joeytwiddle, palavra-chave é "contanto que você use" uma VM madura.
Pacerier
2

Nunca usei, mas existe

JCPP é uma implementação Java completa, compatível, autônoma e pura do pré-processador C. Destina-se a ser útil para pessoas que escrevem compiladores de estilo C em Java usando ferramentas como sablecc, antlr, JLex, CUP e assim por diante. Este projeto foi usado para pré-processar com sucesso grande parte do código-fonte da biblioteca GNU C. A partir da versão 1.2.5, ele também pode pré-processar a biblioteca Apple Objective C.

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

Tom
fonte
1
Não tenho certeza se isso atende às minhas necessidades. Meu código é escrito em Java. Talvez você esteja me propondo obter os códigos-fonte deles e usá-los para pré-processar meu código?
jutky
2

Se você realmente precisa de compilação condicional e usa Ant , pode filtrar seu código e fazer uma busca e substituição nele.

Por exemplo: http://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html

Da mesma forma você pode, por exemplo, escrever um filtro para substituir LOG.debug(...);com /*LOG.debug(...);*/. Isso ainda seria executado mais rápido do que if (LOG.isDebugEnabled()) { ... }outras coisas, sem mencionar que seria mais conciso ao mesmo tempo.

Se você usa o Maven , há um recurso semelhante descrito aqui .

enferrujado
fonte
2

O Manifold fornece um pré-processador Java totalmente integrado (sem etapas de construção ou fonte gerada). Ele visa exclusivamente a compilação condicional e usa diretivas de estilo C.

Preprocessador Java do Manifold

Scott
fonte
1

Usar o Padrão de Fábrica para alternar entre as implementações de uma classe?

O tempo de criação do objeto não pode ser uma preocupação agora, pode? Quando calculada a média de um longo período de tempo de execução, o maior componente de tempo gasto deve estar no algoritmo principal agora, não é?

Estritamente falando, você realmente não precisa de um pré-processador para fazer o que você busca alcançar. Provavelmente, há outras maneiras de atender às suas necessidades além da que propus, é claro.

jldupont
fonte
As mudanças são muito pequenas. Como testar algumas condições para saber com antecedência o resultado solicitado, em vez de recomputá-lo. Portanto, a sobrecarga de chamada para a função pode não ser adequada para mim.
Jutky
0
final static int appFlags = context.getApplicationInfo().flags;
final static boolean isDebug = (appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
alicanbatur
fonte