我有一些非常高性能的关键代码,在高级别上通过执行一些浮点比较或简单谓词来做出一系列决策。代码可以以决策树的形式编写。基本上每个节点都进行谓词检查,然后决定采用哪条路径,直到我们到达某个叶节点。
为了提高性能而不是使用这个决策树,我生成了一些if-else代码块,这些代码块在程序运行时被编译(基本上我为整个决策树生成代码,因为我事先知道谓词)。这确实提高了性能。现在我要做的下一个优化是在运行时修改代码,即在没有代码生成的旧世界中,并且有一个带有节点的决策树,我可以复制树并在一些节点上短路/跳过,从而压缩树并使整体计算更快。但是在生成的代码世界中,是否有任何工具可以通过基于计算期间可用的某些部分运行时数据修改生成的if-else代码来实现相同的功能。还有什么是修改运行时代码的性能含义。
首先,JVM不允许您修改正在运行的代码。它允许您做的是在运行时生成新代码并加载它。
此外,还有一个Oracle赞助的项目(GraalVM),其中(如果我理解正确的话)你可以生成(Truffle)AST并让框架负责代码生成。 (目前似乎是试验性的,它可能尚未准备好用于生产。)。
还有什么是修改运行时代码的性能含义。
那是65,536美元的问题!
一个含义是每次修改代码(通过重新生成)时,必须重新加载方法,并且(对于传统的JVM)再次通过解释和JIT编译阶段。并且可能取决于修改的代码的其他代码的去优化/重新优化。
那不会便宜。每次执行此操作时,我猜测每种方法的数十或数十万本机指令的顺序。
现在我要做的下一个优化是在运行时修改代码......
好。所以我认为你做更复杂的代码生成会更好。
但是,请记住,JIT编译器将优化生成的字节码,根据解释方法时收集的统计信息(在JIT编译之前)执行分支预测等操作。
我还建议您手动尝试一些优化并在实现自己的优化器之前对它们进行基准测试。
这就是我用https://github.com/OpenHFT/Java-Runtime-Compiler的原因
EG
// dynamically you can call
String className = "mypackage.MyClass";
String javaCode = "package mypackage;\n" +
"public class MyClass implements Runnable {\n" +
" public void run() {\n" +
" System.out.println(\"Hello World\");\n" +
" }\n" +
"}\n";
Class aClass = CompilerUtils.CACHED_COMPILER.loadFromJava(className, javaCode);
Runnable runner = (Runnable) aClass.newInstance();
runner.run();
我建议使用一个已知的接口,该接口不会改变,您可以调用并让生成的代码实现接口。
这不支持重新加载类,但每次都可以生成一个新的类名,或者每次都使用不同的类加载器。