【问题标题】:java forcing compile-time evaluationjava强制编译时评估
【发布时间】:2012-02-01 15:16:48
【问题描述】:

在java中,有没有一种强制编译时编译的通用方法?在我参加的编程竞赛中,我们受到程序运行时机器人每轮可以使用的字节码的限制,所以如果我能够在编译时进行计算,我就有优势。

举个具体的例子,假设我想定义一个变量 NORTH,它将是一个数组 MapLocations 表示机器人在朝北时可以看到的方格。如果我手动编码, 我会写:

public class SightSensor{
  public static MapLocation[] NORTH = {new MapLocation(-2,2),
                    new MapLocation(-1,1),
                        new MapLocation(-1,2),
                        new MapLocation(0,1),
                        new MapLocation(0,2),
                        new MapLocation(0,3),
                        new MapLocation(1,1),                       
                        new MapLocation(1,2),
                        new MapLocation(2,2)};

}

表示机器人可以看到正北 90 度弧内的所有正方形,距离为 9 的平方。现在,我的机器人可以看到所有基本方向,有些可以看到更远的地方 相对于其它的。手动编写每个可能的代码将是相当乏味且看似糟糕的形式 视线(距离,方向)对。因此,我写了一个函数

public static MapLocation[] getSensorLocs(int r, Direction dir){ ... bla ... }

自动计算位置,因此我可以将 SightSensor 类重写为

public class SightSensor{
  public static MapLocation[] NORTH = getSensorLocs(3, Direction.NORTH);
}

现在唯一的问题是当机器人尝试使用 NORTH 变量时,它必须花费 运行 getSensorLocs 方法的时间,因此它比手写版本更昂贵。 我可以强制该计算在编译时运行,即在适当的意义上被“符号替换”吗?

【问题讨论】:

  • 我认为您正在寻找一个编译时预处理器,类似于 C 中的那个?
  • 是的,这就是我所希望的。

标签: java bytecode compile-time


【解决方案1】:

现在我了解到您正在优化字节码大小,您可以使用以下方法。

// 8 bytes per field.
public static MapLocation[] NORTH = locations("\u006a\u0079\u007a\u0089\u008a\u008b\u0099\u009a\u009b\u00aa");

// 57 bytes.
public static MapLocation[] locations(String s) {
    final int len = s.length();
    MapLocation[] locations = new MapLocation[len];
    for (int i = 0; i < len; i++) {
        char ch = s.charAt(i);
        locations[i] = new MapLocation((ch >> 4) - 8, (ch & 0xf) - 8);
    }
    return locations;
}

static class MapLocation {
    public MapLocation(int x, int y) {
        System.out.println("x=" + x + ", y=" + y);
    }
}

打印

x=-2, y=2
x=-1, y=1
x=-1, y=2
x=0, y=1
x=0, y=2
x=0, y=3
x=1, y=1
x=1, y=2
x=1, y=3
x=2, y=2

注意:编码字符串的长度不加任何字节码!。


当您运行第一个代码时,它实际上会在运行时创建数组和每个元素。没有编译时优化。我建议您检查您的 getSensorLocs 方法是否以及为什么较慢,因为它不应该明显变慢。

【讨论】:

  • 问题似乎是他运行在一个人工环境中,执行速度严格限制在字节码级别,这使得延迟初始化的东西成为一个真正的问题。
  • 我怀疑他不知道这是个问题,这只是他习惯做的事情。
  • @PeterLawrey,这看起来就像一张票!我现在就调查一下。
  • @andyInCambridge 字符串字面量是常量,与以字节码一次构建一个元素的数组不同。 ;)
【解决方案2】:

public static MapLocation[] NORTH = getSensorLocs(3, Direction.NORTH); 已经只被评估了一次——为什么每次都会运行?你已经完成了“预计算”。

我不确定这是否真的是一个评估问题。您似乎想在编译时运行代码,这不是任何语言一般都会做的事情。 Java 没有预处理器,这可能与您的意思最接近。 Java 做的最多的事情就是计算字面量“5+3”是“8”。

【讨论】:

  • 肖恩,确实我似乎想要一个预处理器命令。没有办法达到手工编码的速度似乎很可悲。我可以编写一个调用 Construct-Sensor 的辅助程序,然后为我编写一个新的“手动编码”SightSensor 类,但这看起来很难看。
  • 许多语言都具有在编译时运行代码的功能,其中 Lisp 宏和 C++ 模板最为突出。
  • C++ 的模板基本上可以在编译时运行代码。我听说它们被用来计算(在编译时)素数或 pi 或类似的东西。但是,afaik,Java 不能做那种有趣的事情。
  • @yshavit 和迈克尔,准确地说。我是一名 C++ 程序员,所以我希望有类似的东西。
  • @andyInCambridge 我相信你知道过早的优化。您能告诉我们在比较性能时它在您的测试中有多大的不同吗?
【解决方案3】:

您要求的不是编译时编译,而是编译时执行。

您要求的是在编译时以某种方式计算 NORTH,然后将其保存为字节码中的“冻结”值。

这通常是不可能或不可取的。

如果您受到可以使用的字节码数量的限制,请考虑将方向等表示为不是对象,而是作为标志数组。这基本上是用 Java 编写老式 C 代码,因此不推荐作为一般最佳实践,但在游戏编程中仍然很常见(尤其是在 Java ME 上)

【讨论】:

    【解决方案4】:

    我相信你能得到的最接近的方法是编写一个在编译期间操作代码的注释处理器,就像在Project Lombok 中所做的那样。但这可能需要大量工作,因为它显然使用了 javac 的非公共 API。

    【讨论】:

      【解决方案5】:

      您可以在另一个 java 项目中编写一个生成 .java 文件的代码生成器。您可以重用对象 (static final MapLocation ML_M2_1 = new MapLocation(-2, 1);),而不是所有那些 new MapLocation,这将有更多帮助。唯一另一种提高速度的方法是考虑您正在创建查找数据。如果你成功地为new MapLocation(-1, 2) 创建了一个像0xF2 这样的数字编码,你真的会节省一些东西。

      【讨论】:

      • 没错。代码生成器可能是我唯一的选择。本质上,我问的是如何在 Java 中构建一个高效的查找表。你知道我可以参考的任何材料吗?
      猜你喜欢
      • 1970-01-01
      • 2020-08-22
      • 2012-12-26
      • 1970-01-01
      • 2017-12-22
      • 2011-09-27
      • 1970-01-01
      • 2016-06-28
      • 1970-01-01
      相关资源
      最近更新 更多