我在java中做了一些实验,我遇到了一件令我烦恼的事情。我已经意识到在java中,当我使用方法而不是直接代码时,处理它需要更多的时间。
我有以下代码:
public static void main(String[] args) {
long nanoSeconds = System.nanoTime();
int i = foo();
System.out.println(i);
System.out.println("Elapsed Nanoseconds = " + (System.nanoTime() - nanoSeconds));
nanoSeconds = System.nanoTime();
int l = 10;
i = l;
System.out.println(i);
System.out.println("Elapsed Nanoseconds = " + (System.nanoTime() - nanoSeconds));
}
public final static int foo() {
int i = 10;
return i;
}
这是一个简单的代码,分为两部分。第一个测量foo()的时间并显示foo()的返回值,第二个部分执行相同但不调用foo()。
结果如下:
10
经过的纳秒= 601582
10
经过的纳秒= 49343
所以我的问题是,是否有办法不放松这种表现?
谢谢大家。
答案 0 :(得分:12)
您不会以这种方式获得任何有意义的基准。
您不会考虑JIT。
编译器除了非常明显的优点外,不会在这方面进行任何优化;当它在源代码中看到一个方法调用时,即使这个方法调用总是返回相同的值,它也会生成调用该方法的字节码;当它看到一个常量时,它将生成一个ldc(加载常量)字节码指令。
BUT。
然后JIT在某个时候开始。如果它确定方法调用总是返回相同的结果,那么它将内联调用。在运行时。但这只有在执行了一定数量的代码执行后才能完成,并且如果它承认它在某些时候错过了(这是回溯),它总会有回路。
这只是一个优秀的JIT实现可以执行的优化。
您想要观看this video。简而言之:使用Oracle的JVM,优化将在一段代码至少执行10000次之后开始启动 - 用于某些代码的定义"。
答案 1 :(得分:5)
以下是您在JMH上测量的代码:
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
@OperationsPerInvocation(Measure.SIZE)
@Warmup(iterations = 10, time = 100, timeUnit=MILLISECONDS)
@Measurement(iterations = 5, time = 200, timeUnit=MILLISECONDS)
@State(Scope.Thread)
@Threads(1)
@Fork(1)
public class Measure
{
public static final int SIZE = 1;
@Benchmark public int call() {
int i = foo();
return i;
}
@Benchmark public int callDisableOptimization() {
int i = fooDontInline();
return i;
}
@Benchmark public int inline() {
int i;
int l = 10;
i = l;
return i;
}
static int foo() {
int i = 10;
return i;
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
static int fooDontInline() {
int i = 10;
return i;
}
}
说明:
call
对应您的第一次测量,定期致电foo()
; inline
对应于您的第二次测量,您可以在其中内联foo()
的逻辑而不涉及任何调用; callDisableOptimization
是第一种情况的特殊转折,我们使用高级低级控制来禁用JVM的自动内联。这些是结果:
Benchmark Mode Samples Score Error Units
o.s.Measure.call avgt 5 1,279 ± 0,114 ns/op
o.s.Measure.inline avgt 5 1,289 ± 0,100 ns/op
o.s.Measure.callDisableOptimization avgt 5 3,167 ± 0,217 ns/op
注意事项:
关于衡量JVM性能的方法的一些评论:
println
操作一起包含在内,至少需要微秒答案 2 :(得分:0)
注意:通过只调用一次方法,您无法准确地测量这种假设的时间。您必须至少多次这样做以获得可用的结果。
是的,由于涉及更多指令,调用方法的速度较慢。当代码在另一个函数内时,它可以简单地在那里执行。调用函数时,会创建一个新的堆栈帧,并在返回后删除。这需要一点时间 - 至少在例如JIT开始并编译所述部分之前。您应该查看JVM specifications以获取更多信息。