Java中的#ifdef #ifndef


106

我怀疑是否有一种方法可以在Java中创建编译时条件,例如C ++中的#ifdef #ifndef。

我的问题是,有一种用Java编写的算法,并且对该算法的运行时间有所不同。因此,我想衡量每次使用改进后可以节省多少时间。

现在,我有一组布尔变量,这些变量用于确定在运行时应使用哪个改进,而不是哪个改进。但是,即使测试这些变量也会影响总运行时间。

因此,我想找到一种方法来确定在编译期间应编译和使用程序的哪些部分。

有人知道用Java做到这一点的方法吗?或者,也许有人知道没有这种方法(这也很有用)。

Answers:


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"));

然后,JIT编译器将评估任何依赖enableFast的条件。此开销可忽略不计。


这个解决方案比我的更好。当我尝试使用预设的外部值初始化变量时,运行时间回到了3秒。但是,当我将变量定义为静态类变量(而不是函数局部变量)时,运行时间返回到1秒。谢谢您的帮助。
jutky

6
IIRC,甚至在Java使用JIT编译器之前就可以使用。javac我认为该代码已删除。仅当(say)的表达式enableFast是编译时常量表达式时,此方法才有效。
斯蒂芬·C

2
是的,但是这个条件必须存在于方法中,对吗?如果我们要设置一堆私有静态最终字符串,那该怎么办?(例如,针对生产和登台设置不同的一组服务器URL)
tomwhipple 2011年

3
@tomwhipple:真正的,再加上这不会让你做这样的事情: private void foo(#ifdef DEBUG DebugClass obj #else ReleaseClass obj #endif )
佐科

3
导入(例如,关于类路径)又如何呢?
n611x007 2014年

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 2014年

@pacerier,我不知道JLS是否可以保证,但是对于90年代以来遇到的每个Java编译器都是如此,可能是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

大!然后,我认为这必须是运行时优化,实际上并没有在编译时剥离代码。只要使用成熟的VM,就可以了。
joeytwiddle

@joeytwiddle,关键字是“只要您使用”成熟的VM。
Pacerier 2014年

2

从未使用过,但是存在

JCPP是C预处理程序的完整,兼容,独立,纯Java实现。它旨在供使用sablecc,antlr,JLex,CUP等工具用Java编写C风格编译器的人使用。该项目已被用来成功预处理GNU C库的许多源代码。从1.2.5版开始,它还可以预处理Apple Objective C库。

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


1
我不确定这是否适合我的需求。我的代码是用Java编写的。也许您是在提议我获取他们的资源并使用它们来预处理我的代码?
jutky


2

Manifold提供了一个完全集成的Java预处理器(没有构建步骤或生成的源代码)。它专门针对条件编译,并使用C风格的指令。

流形的Java预处理器


1

使用工厂模式在类的实现之间切换?

现在可以不用担心对象创建时间了吗?如果在很长一段时间内平均,花费的时间的最大部分应该在主算法中,不是吗?

严格来说,您实际上不需要预处理器即可完成您想要实现的目标。当然,除了我建议的方法以外,还有其他可能满足您要求的方法。


所做的更改很小。像测试某些条件一样,可以提前知道请求的结果,而不用重新计算。因此,调用该函数的开销可能不适合我。
jutky

0
final static int appFlags = context.getApplicationInfo().flags;
final static boolean isDebug = (appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.