我正在为Java中的数据结构编写一组可视化接口。我们的想法是这些类应该是算法的高性能实现,但是使用嵌入式钩子,这样算法就可以交互显示。
有很多理由要这样做,但是如果你接受这个面值的请求,我想在算法中嵌入调用来识别刚刚完成的特定子部分。例如,一次排序算法。
我更希望图书馆既高效又允许这样做。在C ++中,我会插入两个不同的模板或使用条件编译,并且可以合理地生成两个版本的代码。有没有办法在Java中实现这一点?我希望别人可以提出一个,因为我做不到。
新闻快讯。我试过这个实际的代码。
对于n = 100,000,插入排序大约需要9800毫秒,其中VISUALIZE作为静态变量但不是最终的,而大约3100表示已注释掉。因此,性能损失是不可接受的。
使用visualize作为静态final,优化器确实检测到它并将其删除,但鉴于它是最终的,我可以用它做什么?我不能动态打开和关闭可视化!
public class TestSort {
private static boolean VISUALIZE = false;
private static ArrayObserver ao;
public static void insertionSort(int[] x) {
for (int i = 1; i < x.length; i++) {
int temp = x[i];
int j = i - 1;
if (x[j] > temp) {
do {
x[j+1] = x[j];
/* if (VISUALIZE) {
ao.compare(i-1, i);
ao.copy(i-1, i);
}*/
} while (--j >= 0 && x[j] > temp);
x[j+1] = temp;
}
}
}
static Random r = new Random();
static int[] createRandomArray(int n) {
int[] x = new int[n];
for (int i = 0; i < x.length; i++)
x[i] = r.nextInt(100);
return x;
}
static void display(int[] x) {
for (int i = 0; i < x.length; i++)
System.out.print(x[i] + " ");
System.out.println();
}
public static void main(String args[]) {
//int[] x = {9, 8, 7, 6, 5, 4, 3, 2, 1};
int [] x = createRandomArray(100000);
ao = new ArrayObserver(x);
if (x.length < 20) display(x);
long t0 = System.currentTimeMillis();
insertionSort(x);
long t1 = System.currentTimeMillis();
if (x.length < 20) display(x);
System.out.println(t1-t0);
}
}
答案 0 :(得分:5)
我认为你有几个选择:
答案 1 :(得分:2)
与例如相反C ++,对本机机器代码的最终编译是在运行时完成的,在这种情况下,出于性能原因,完全不需要构建两个单独的版本。
如果您传递boolean
以启用/禁用额外调用作为参数,以实现您的算法的类的构造函数并将其存储在final
类变量(即常量)中,当算法在紧密循环(=热点&#39;)中执行时,Hotspot VM将编译类实例并删除死代码。这种运行时优化不能用C ++完成。
但请注意,boolean
测试可能只占整个算法的一小部分。
编辑:您的测试表明这不起作用,但我不确定它们是否正确完成。您没有使用任何基准测试框架。最积极的优化将发生在服务器VM(-server
)上,然后代码必须首先正确预热(前10000次左右的迭代将发生未编译,当然要慢得多)。此外,使用模板模式可能比final boolean
更有可能进行优化,因为布尔检查无论如何都是便宜的,并且已知编译器进行虚拟调用内联(据我所知)。
EDIT2:如果您不需要在运行时切换(在完成所有条件编译和单独的构建之后也不会帮助您),只需使用您知道已优化的static final boolean
。使用命令行参数或配置文件中的值对其进行初始化,您可以在应用程序启动时轻松地在两个版本之间切换。
答案 2 :(得分:1)
如果你这样做:
private static final ENABLED = false;
// Then later...
if(ENABLED){
call();
}
整个if-block甚至不会包含在生成的字节码中(至少在较新的JVM上)。这会是一种选择吗?
答案 3 :(得分:0)
Java没有预处理器的原因是为了防止程序员完全按照您的要求进行操作。相反,您应该编写Java代码来实现您在预处理器指令中实现的功能,并让编译器优化生成的代码。这样,您将最终得到用单一语言(而不是两种语言,Java和预处理器DSL)编写的代码,这使您在分析代码时使用的工具链变得更加容易。
在纯Java代码中解决问题的一种方法是使用Template method pattern。
答案 4 :(得分:0)
当this guy声称要证明时,可以修改字节码以在运行时设置最终变量。我想你可以使用相同的方法来设置static final VISUALIZE
开启和关闭。