Lat*_*ter 2 java jvm latency real-time hotspot
下面的简短代码可以解决问题.基本上我是计时方法addToStorage.我开始执行它一百万次,我能够将它的时间缩短到大约723纳秒.然后我做一个短暂的暂停(使用繁忙的旋转方法不释放cpu核心)并在另一个代码位置再次计算方法N次.令我惊讶的是,我发现N越小,addToStorage延迟越大.
例如:
If N = 1 then I get 3.6 micros
If N = 2 then I get 3.1 and 2.5 micros
if N = 5 then I get 3.7, 1.8, 1.7, 1.5 and 1.5 micros
Run Code Online (Sandbox Code Playgroud)
有谁知道为什么会这样,以及如何解决它?我希望我的方法能够在最快的时间内始终如一地执行,无论我在哪里调用它.
注意:我不认为它与线程有关,因为我没有使用Thread.sleep.我还测试了使用taskset相同的结果将我的线程固定到cpu核心.
import java.util.ArrayList;
import java.util.List;
public class JvmOdd {
private final StringBuilder sBuilder = new StringBuilder(1024);
private final List<String> storage = new ArrayList<String>(1024 * 1024);
public void addToStorage() {
sBuilder.setLength(0);
sBuilder.append("Blah1: ").append(System.nanoTime()).append('\n');
sBuilder.append("Blah2: ").append(System.nanoTime()).append('\n');
sBuilder.append("Blah3: ").append(System.nanoTime()).append('\n');
sBuilder.append("Blah4: ").append(System.nanoTime()).append('\n');
sBuilder.append("Blah5: ").append(System.nanoTime()).append('\n');
sBuilder.append("Blah6: ").append(System.nanoTime()).append('\n');
sBuilder.append("Blah7: ").append(System.nanoTime()).append('\n');
sBuilder.append("Blah8: ").append(System.nanoTime()).append('\n');
sBuilder.append("Blah9: ").append(System.nanoTime()).append('\n');
sBuilder.append("Blah10: ").append(System.nanoTime()).append('\n');
storage.add(sBuilder.toString());
}
public static long mySleep(long t) {
long x = 0;
for(int i = 0; i < t * 10000; i++) {
x += System.currentTimeMillis() / System.nanoTime();
}
return x;
}
public static void main(String[] args) throws Exception {
int warmup = Integer.parseInt(args[0]);
int mod = Integer.parseInt(args[1]);
int passes = Integer.parseInt(args[2]);
int sleep = Integer.parseInt(args[3]);
JvmOdd jo = new JvmOdd();
// first warm up
for(int i = 0; i < warmup; i++) {
long time = System.nanoTime();
jo.addToStorage();
time = System.nanoTime() - time;
if (i % mod == 0) System.out.println(time);
}
// now see how fast the method is:
while(true) {
System.out.println();
// Thread.sleep(sleep);
mySleep(sleep);
long minTime = Long.MAX_VALUE;
for(int i = 0; i < passes; i++) {
long time = System.nanoTime();
jo.addToStorage();
time = System.nanoTime() - time;
if (i > 0) System.out.print(',');
System.out.print(time);
minTime = Math.min(time, minTime);
}
System.out.println("\nMinTime: " + minTime);
}
}
}
Run Code Online (Sandbox Code Playgroud)
执行:
$ java -server -cp . JvmOdd 1000000 100000 1 5000
59103
820
727
772
734
767
730
726
840
736
3404
MinTime: 3404
Run Code Online (Sandbox Code Playgroud)
小智 5
这里有很多事情,我不知道从哪里开始.但是我们从这里开始......
long time = System.nanoTime();
jo.addToStorage();
time = System.nanoTime() - time;
Run Code Online (Sandbox Code Playgroud)
使用此技术无法测量addToStoarge()的延迟.它只是运行得太快意味着你可能低于时钟的分辨率.如果不运行这个,我猜测你的测量是由时钟边缘计数决定的.您需要增加工作单元以获得噪声水平较低的测量.
至于发生了什么?有许多调用站点优化是最重要的内联.内联将完全消除调用站点,但它是路径特定的优化.如果从不同的地方调用该方法,那将遵循执行虚方法查找然后跳转到该代码的慢速路径.因此,要从不同的路径看到内联的好处,那条路径也必须"预热".
我强烈建议您同时查看JMH(随JDK提供).那里有黑洞等设施可以帮助减少CPU时钟的影响.您可能还想在JITWatch(Adopt OpenJDK项目)等工具的帮助下评估工作台的质量,这些工具将获取JIT生成的日志并帮助您中断它们.