【问题标题】:#ifdef #ifndef in Java#ifdef #ifndef 在 Java 中
【发布时间】:2023-03-28 01:50:02
【问题描述】:

我怀疑是否有办法在 Java 中生成编译时条件,例如 C++ 中的 #ifdef #ifndef。

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

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

所以我想找到一种方法来决定在编译期间应该编译和使用程序的哪些部分。

有人知道用 Java 做的方法吗?或者也许有人知道没有这样的方法(它也很有用)。

【问题讨论】:

    标签: java compilation conditional conditional-compilation


    【解决方案1】:

    没用过,但是有这个

    JCPP 是一个完整的、合规的、 独立的纯 Java 实现 C 预处理器。它的目的是 对编写 C 风格的人有用 Java中的编译器使用类似的工具 sablecc、antlr、JLex、CUP 等 向前。该项目已使用 成功地预处理大部分 GNU C 库的源代码。作为 1.2.5版本的,它也可以 预处理 Apple Objective C 图书馆。

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

    【讨论】:

    • 我不确定这是否适合我的需要。我的代码是用 Java 编写的。也许您建议我获取他们的资源并使用它们来预处理我的代码?
    【解决方案2】:

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

    现在可以不用担心对象创建时间了吧?当在较长的运行时间段内平均时,花费时间的最大组成部分现在应该是在主算法中,不是吗?

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

    【讨论】:

    • 变化很小。就像测试一些条件以提前知道请求的结果而不是重新计算它。所以调用函数的开销可能不适合我。
    【解决方案3】:

    javac 不会输出无法访问的编译代码。为您的#define 使用设置为常量值的最终变量,为#ifdef 使用普通的if 语句。

    您可以使用 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
    

    【讨论】:

    • 这是 javac 特有的,还是 JLS 实际上保证了这种行为?
    • @pacerier,我不知道 JLS 是否能保证这一点,但自 90 年代以来我遇到的每个 java 编译器都是如此,除了 1.1.7 之前的可能例外,只是因为我当时没有测试它。
    【解决方案4】:
    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 秒。感谢您的帮助。
    • IIRC,这甚至在 Java 有 JIT 编译器之前就可以工作。我认为该代码已被javac 删除。这仅在(比如)enableFast 的表达式是编译时常量表达式时才有效。
    • 是的,但是这个条件必须存在于方法中,对吗?如果我们有一堆我们想要设置的私有静态最终字符串,那该怎么办。 (例如,一组服务器 URL 为生产与暂存设置不同)
    • @tomwhipple :是的,而且这不允许你做类似的事情:private void foo(#ifdef DEBUG DebugClass obj #else ReleaseClass obj #endif )
    • 导入怎么样(例如,关于类路径)?
    【解决方案5】:

    我想我已经找到了解决方案,它要简单得多。
    如果我用“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++;
        }
    

    【讨论】:

    • 这很有趣。听起来 JIT 已经支持条件编译了!如果这些决赛在另一个类或另一个包中,它会起作用吗?
    • 太棒了!然后我相信这一定是运行时优化,代码实际上并没有在编译时被剥离。只要您使用成熟的虚拟机就可以了。
    • @joeytwiddle,关键字是“只要你使用”成熟的虚拟机。
    【解决方案6】:

    如果您确实需要条件编译并且使用 Ant,则可以过滤您的代码并在其中进行搜索和替换。

    例如:http://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html

    以同样的方式,例如,您可以编写一个过滤器,将LOG.debug(...); 替换为/*LOG.debug(...);*/。这仍然会比if (LOG.isDebugEnabled()) { ... } 的东西执行得更快,更不用说同时更简洁了。

    如果您使用 Maven,则描述了类似的功能 here

    【讨论】:

      【解决方案7】:
      final static int appFlags = context.getApplicationInfo().flags;
      final static boolean isDebug = (appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
      

      【讨论】:

        【解决方案8】:

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

        【讨论】:

          【解决方案9】:

          如果您使用 IntelliJ,则有一个名为 Manifold 的插件,它与许多其他功能一起允许在 Java 中使用 #ifdef#define

          插件网址: https://manifold.systems/

          预处理器信息: https://github.com/manifold-systems/manifold/tree/master/manifold-deps-parent/manifold-preprocessor

          PS:我不隶属于他们,我们只是碰巧使用它,它在没有工作流的情况下有很大帮助(这对于 Java 开发来说可能不是典型的)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-04-14
            • 1970-01-01
            • 2012-12-27
            • 2013-02-11
            • 1970-01-01
            • 2017-02-25
            • 2022-01-20
            相关资源
            最近更新 更多