我知道Java反射很慢,但是为什么它的执行时间如此不稳定呢?

huangapple go评论76阅读模式
英文:

I know Java Reflection is slow, but why is its execution time so unstable?

问题

我一直在测试Java反射,并且我发现,在使用execute()方法时,完成该方法所需的时间差异很大。

以下是我正在使用的代码:

int nTest = 101;
long mean = 0;
long[] execTime = new long[nTest - 1];

for (int i = 0; i < nTest; i++) {
     long tStart = System.nanoTime();
     metodin.invoke(o, parameters);
     long tFinal = System.nanoTime();
     long tDiff = (tFinal - tStart) / 100;

     if (i > 0)               // 仅为防止首次执行时间较长而舍弃第一次执行。
         execTime[i - 1] = tDiff;
}

long totalExecutionTime = 0;

for (int i = 0; i < numbers.length; i++) {
    totalExecutionTime += execTime[i];
    // System.out.println(execTime[i]);
}
mean = totalExecutionTime / execTime.length;

以下是多个示例之一,其中我们可以看到时间差异很大(使用注释的println语句获得的输出):

24
23
20
21
21
[...]
22
22
23
60
23
19
20
13028 // BOOM!!!!!!!
496 // 低性能持续一段时间...
160
115
116
120
114
123
121
115
120
114
114
127
323
40 // 返回正常状态
27
25
23
18
35

这种巨大的差异使得平均值无法反映方法的实际情况。为了解决这个问题,我尝试对该方法执行了1000多次,但似乎这种"BOOM"现象是周期性的。为什么会发生这种情况呢?

英文:

I've been testing java reflection and I found that, when using execute(), the time needed to finish the method varies a lot.

Here is the code that I'm using:

int nTest = 101;
long mean = 0;
long[] execTime = new long[nTest - 1];

for (int i = 0; i &lt; nTest; i++) {
     long tStart = System.nanoTime();
     metodin.invoke(o, parameters);
     long tFinal = System.nanoTime();
     long tDiff = (tFinal - tStart) / 100;

     if (i &gt; 0)               // I discard the first execution just in case it takes a lot of time.
         execTime[i - 1] = tDiff;
}

long totalExecutionTime = 0;

for (int i = 0; i &lt; numbers.length; i++) {
    totalExecutionTime += execTime[i];
    // System.out.println(execTime[i]);
}
mean = totalExecutionTime / execTime.length;

And here's one of the multiple examples where we can see a huge variation in time (print obtained using the commented println):

24
23
20
21
21
[...]
22
22
23
60
23
19
20
13028 // BOOM!!!!!!!
496 // and the low performance continues for a while...
160
115
116
120
114
123
121
115
120
114
114
127
323
40 // returns to normal
27
25
23
18
35

The variation is so big that it makes the mean not reflect the reality of the method.
To solve this problem I was executing over 1000 times the method, but it seems that this "BOOM" is kind of cyclic.
Why is this happening?

答案1

得分: 2

这在Java中是一个普遍的情况,至少在HotSpot JVM上是如此,即使没有涉及到反射,代码执行时间也会呈现出这种变化。这是由于JIT编译器——Java代码在运行时进行优化,找出哪些方法对性能至关重要,并且仅针对整个程序中最常运行的代码进行优化,这基于它在实际中的使用方式。你观察到的情况可能是实现的交换,然后逐渐优化并改善了新实现上的分支预测。

这使得Java代码比那些没有关于代码实际运行方式的任何信息的预编译语言更快,但会带来“热身时间”和在基准测试中的复杂性。这还需要考虑垃圾收集,它在任意时间发生,可能会在程序运行时减慢程序的速度。

英文:

It is universally the case in Java -- at least on the HotSpot JVM -- that code execution times will exhibit variance like this, even if reflection isn't involved. This is due to the JIT compiler -- Java code gets optimized at runtime, finding which methods are performance critical and only optimizing the code that is run the most often within the whole program, based on how it gets used in practice. The point you observed is probably a swap in implementations and then gradually optimizing and improving branch prediction on the new implementation.

This can make Java code faster than precompiled languages that don't have any information on how the code gets run in practice, with the consequence of "warm up time" and complexity in benchmarking. This is in addition to garbage collection, which happens at arbitrary times and can slow down your program while it's running.

huangapple
  • 本文由 发表于 2020年10月3日 15:55:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/64181959.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定