您可能能够提高测量精度的另一种方法是:
- 以纳秒为单位进行计算,因此经过的时间(使用
System.nanoTime()
和 CPU 时间本机以纳秒为单位。阻塞和等待时间应该转换,因为它们以毫秒为单位。
- 实现简单捕获经过时间、cpu 时间、阻塞时间和等待时间所需时间的近似值。(称之为 OVERHEAD。)您可以在某处的静态块中执行此操作。首先做一个 JVM 预热循环,记住 JIT 在 15000 次方法调用后启动 [默认情况下?取决于您的 JVM....],因此至少该大小的预热循环会很好。
- 运行预热后循环,将经过的总时间除以循环计数,您将获得 OVERHEAD 值。
- 从计算的方法经过时间中减去 OVERHEAD 以更好地了解方法的代码执行时间,或者....
- 将 OVERHEAD 添加到 CPU、Blocked 和 Waited 以更接近实际的挂墙时间。
- 可以选择不时重新计算开销。
它并不完美,当然也不严格,但它可能会给你带来更好的数字。
这是我运行的一些测试代码来计算我的平均开销(Java 7、Windows 7、64 位)。我试图确保没有省略任何方法,但你的里程可能会有所不同。
public class Overhead {
static final ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
public static void main(String[] args) {
tmx.setThreadContentionMonitoringEnabled(true);
tmx.setThreadCpuTimeEnabled(true);
int loops = 15000;
long sum = -1;
long start = System.nanoTime();
for(int i = 0; i < loops; i++) {
sum = measure();
}
long elapsed = System.nanoTime()-start;
log("Warmup completed in [" + elapsed + "] ns. ");
log("Sum:" + sum);
start = System.nanoTime();
loops = loops * 2;
for(int i = 0; i < loops; i++) {
sum = measure();
}
elapsed = System.nanoTime()-start;
long avg = (elapsed/loops);
log("Test completed in [" + elapsed + "] ns. OVERHEAD: [" + avg + "] ns.");
log("Sum:" + sum);
}
protected static long measure() {
long s1 = System.nanoTime();
long bt = tmx.getCurrentThreadCpuTime();
ThreadInfo ti = tmx.getThreadInfo(Thread.currentThread().getId());
long blocked = ti.getBlockedTime();
long waited = ti.getWaitedTime();
long s2 = System.nanoTime();
return ((s2 - s1) + blocked + waited + bt);
}
public static void log(Object msg) {
System.out.println(msg);
}
}
我的输出如下:
Overhead test
Warmup completed in [43176164] ns.
Sum:109201929
Test completed in [38482368] ns. OVERHEAD: [1282] ns.
Sum:156002228