当注释掉从未执行过的代码时,Java程序运行得较慢
我在我的一个Java程序中观察到一些奇怪的行为。我试图尽可能地去除代码,同时仍然能够复制行为。下面是完整的代码当注释掉从未执行过的代码时,Java程序运行得较慢,java,recursion,compiler-optimization,Java,Recursion,Compiler Optimization,我在我的一个Java程序中观察到一些奇怪的行为。我试图尽可能地去除代码,同时仍然能够复制行为。下面是完整的代码 public class StrangeBehaviour { static boolean recursionFlag = true; public static void main(String[] args) { long startTime = System.nanoTime(); for (int i = 0; i <
public class StrangeBehaviour {
static boolean recursionFlag = true;
public static void main(String[] args) {
long startTime = System.nanoTime();
for (int i = 0; i < 10000; i ++) {
functionA(6, 0);
}
long endTime = System.nanoTime();
System.out.format("%.2f seconds elapsed.\n", (endTime - startTime) / 1000.0 / 1000 / 1000);
}
static boolean functionA(int recursionDepth, int recursionSwitch) {
if (recursionDepth == 0) { return true; }
return functionB(recursionDepth, recursionSwitch);
}
static boolean functionB(int recursionDepth, int recursionSwitch) {
for (int i = 0; i < 16; i++) {
if (StrangeBehaviour.recursionFlag) {
if (recursionSwitch == 0) {
if (functionA(recursionDepth - 1, 1 - recursionSwitch)) return true;
} else {
if (!functionA(recursionDepth - 1, 1 - recursionSwitch)) return false;
}
} else {
// This block is never entered into.
// Yet commenting out one of the lines below makes the program run slower!
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
}
}
return false;
}
}
公共类行为{
静态布尔递归标志=true;
公共静态void main(字符串[]args){
long startTime=System.nanoTime();
对于(int i=0;i<10000;i++){
功能性(6,0);
}
long-endTime=System.nanoTime();
System.out.format(“%.2f秒已过。\n”,(endTime-startTime)/1000.0/1000/1000);
}
静态布尔函数(int递归深度,int递归开关){
如果(recursionDepth==0){return true;}
返回函数B(递归深度、递归开关);
}
静态布尔函数B(int recursionDepth,int recursionSwitch){
对于(int i=0;i<16;i++){
if(奇怪行为.递归标志){
如果(递归开关==0){
if(function(recursionDepth-1,1-recursionSwitch))返回true;
}否则{
如果(!function(recursionDepth-1,1-recursionSwitch))返回false;
}
}否则{
//这个区块永远不会进入。
//然而,注释掉下面的一行会使程序运行变慢!
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
System.out.println(“…”);
}
}
返回false;
}
}
我有两个函数,function()
和functionB()
递归调用。这两个函数都采用控制递归终止的recursionDepth
参数functionA()
调用functionB()
最多一次,且recursionDepth
保持不变functionB()
使用递归深度-1调用function()
16次。当使用0
的recursionDepth
调用function()
时,递归终止
functionB()
有一个包含大量System.out.println()调用的代码块。此块永远不会被输入,因为输入由布尔递归标志
变量控制,该变量设置为true
,并且在程序执行期间从未更改。但是,注释掉一个println()
调用会导致程序运行较慢。在我的机器上,当其中一个调用被注释掉时,执行时间是2秒
是什么导致了这种行为?我唯一的猜测是,与代码块长度(或函数调用数等)相关的参数触发了一些幼稚的编译器优化。如果您对此有进一步的了解,我们将不胜感激
编辑:我正在使用JDK1.8。注释的代码会影响内联的处理方式。
如果functionB变得更长/更大(字节码指令更多),它将不会内联到functionA中
因此@J3D1能够使用VMOptions手动关闭functionB()的内联:-XX:CompileCommand=dontinline,com.jd.benchmarking.skangebehaviour::functionB
这似乎可以用较短的函数消除延迟
使用vm选项,您可以显示内联
-XX:+UnlockDiagnosticVMOptions-XX:+PrintInline
更大的版本,不会内联函数B
较短的版本将尝试内联functionB,导致进一步尝试。
主要是猜测,但较大的/内联字节码将导致分支预测和缓存问题我是@k5_389;,似乎存在一个阈值来确定是否内联函数。如果JIT编译器决定内联它,它将导致大量的工作和时间,如-XX:+printcomilation
所示:
task-id
158 32 3 so_test.StrangeBehaviour::functionB (326 bytes) made not entrant
159 35 3 java.lang.String::<init> (82 bytes)
160 36 s 1 java.util.Vector::size (5 bytes)
1878 37 % 3 so_test.StrangeBehaviour::main @ 6 (65 bytes)
1898 38 3 so_test.StrangeBehaviour::main (65 bytes)
2665 39 3 java.util.regex.Pattern::has (15 bytes)
2667 40 3 sun.misc.FDBigInteger::mult (64 bytes)
2668 41 3 sun.misc.FDBigInteger::<init> (30 bytes)
2668 42 3 sun.misc.FDBigInteger::trimLeadingZeros (57 bytes)
2.51 seconds elapsed.
新时间和JIT编译器信息:
140 34 3 so_test.StrangeBehaviour::functionB (326 bytes) made not entrant
145 36 3 java.lang.String::<init> (82 bytes)
148 37 s 1 java.util.Vector::size (5 bytes)
162 38 4 so_test.StrangeBehaviour::functionA (12 bytes)
163 33 3 so_test.StrangeBehaviour::functionA (12 bytes) made not entrant
1916 39 % 3 so_test.StrangeBehaviour::main @ 6 (65 bytes)
1936 40 3 so_test.StrangeBehaviour::main (65 bytes)
2686 41 3 java.util.regex.Pattern::has (15 bytes)
2689 42 3 sun.misc.FDBigInteger::mult (64 bytes)
2690 43 3 sun.misc.FDBigInteger::<init> (30 bytes)
2690 44 3 sun.misc.FDBigInteger::trimLeadingZeros (57 bytes)
2.55 seconds elapsed.
140 34 3 so_测试。奇怪行为::函数B(326字节)未进入
145 36 3 java.lang.String::(82字节)
148 37 s1 java.util.Vector::size(5字节)
162 38 4 so_测试。奇怪行为::函数(12字节)
163 33 3 so_测试。奇怪行为::函数(12字节)未进入
1916 39%3 so_测试。奇怪行为::main@6(65字节)
1936 40 3 so_测试。奇怪行为::main(65字节)
2686 41 3 java.util.regex.Pattern::has(15字节)
2689 42 3 sun.misc.FDB
task-id
158 32 3 so_test.StrangeBehaviour::functionB (326 bytes) made not entrant
159 35 3 java.lang.String::<init> (82 bytes)
160 36 s 1 java.util.Vector::size (5 bytes)
1878 37 % 3 so_test.StrangeBehaviour::main @ 6 (65 bytes)
1898 38 3 so_test.StrangeBehaviour::main (65 bytes)
2665 39 3 java.util.regex.Pattern::has (15 bytes)
2667 40 3 sun.misc.FDBigInteger::mult (64 bytes)
2668 41 3 sun.misc.FDBigInteger::<init> (30 bytes)
2668 42 3 sun.misc.FDBigInteger::trimLeadingZeros (57 bytes)
2.51 seconds elapsed.
task-id
126 35 4 so_test.StrangeBehaviour::functionA (12 bytes)
130 33 3 so_test.StrangeBehaviour::functionA (12 bytes) made not entrant
131 36 s 1 java.util.Vector::size (5 bytes)
14078 37 % 3 so_test.StrangeBehaviour::main @ 6 (65 bytes)
14296 38 3 so_test.StrangeBehaviour::main (65 bytes)
14296 39 % 4 so_test.StrangeBehaviour::functionB @ 2 (318 bytes)
14300 40 4 so_test.StrangeBehaviour::functionB (318 bytes)
14304 34 3 so_test.StrangeBehaviour::functionB (318 bytes) made not entrant
14628 41 3 java.util.regex.Pattern::has (15 bytes)
14631 42 3 sun.misc.FDBigInteger::mult (64 bytes)
14632 43 3 sun.misc.FDBigInteger::<init> (30 bytes)
14632 44 3 sun.misc.FDBigInteger::trimLeadingZeros (57 bytes)
14.50 seconds elapsed.
if (StrangeBehaviour.recursionFlag) {
int a = 1;
int b = 1;
if (recursionSwitch == 0) {
if (functionA(recursionDepth - 1, 1 - recursionSwitch)) return true;
} else {
if (!functionA(recursionDepth - 1, 1 - recursionSwitch)) return false;
}
} else {
// This block is never entered into.
// Yet commenting out one of the lines below makes the program run slower!
System.out.println("...");
System.out.println("...");
System.out.println("...");
//System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
System.out.println("...");
}
140 34 3 so_test.StrangeBehaviour::functionB (326 bytes) made not entrant
145 36 3 java.lang.String::<init> (82 bytes)
148 37 s 1 java.util.Vector::size (5 bytes)
162 38 4 so_test.StrangeBehaviour::functionA (12 bytes)
163 33 3 so_test.StrangeBehaviour::functionA (12 bytes) made not entrant
1916 39 % 3 so_test.StrangeBehaviour::main @ 6 (65 bytes)
1936 40 3 so_test.StrangeBehaviour::main (65 bytes)
2686 41 3 java.util.regex.Pattern::has (15 bytes)
2689 42 3 sun.misc.FDBigInteger::mult (64 bytes)
2690 43 3 sun.misc.FDBigInteger::<init> (30 bytes)
2690 44 3 sun.misc.FDBigInteger::trimLeadingZeros (57 bytes)
2.55 seconds elapsed.
public static void main(String[] args) {
// Added the following warmup loop before the timing loop
for (int i = 0; i < 50000; i++) {
functionA(6, 0);
}
long startTime = System.nanoTime();
for (int i = 0; i < 50000; i++) {
functionA(6, 0);
}
long endTime = System.nanoTime();
System.out.format("%.2f seconds elapsed.\n", (endTime - startTime) / 1000.0 / 1000 / 1000);
}