为什么Java中的确定性算法在不同的时间运行?

为什么Java中的确定性算法在不同的时间运行?,java,multithreading,jvm,performance,deterministic,Java,Multithreading,Jvm,Performance,Deterministic,我有一个Java程序来计算n体问题。在每次迭代中,它检查每个物体对其他物体施加的力,然后根据力移动它们 物体总是从同一个点开始(我把它们排列成一个圆圈,从物体0到物体n),它们总是按照相同的顺序进行检查和移动(从物体0到n)。然而,当我运行程序30次时,我得到的运行时间却截然不同。一个运行时间为2947188毫秒(49分钟),另一个运行时间为920967毫秒(15分钟)。我对这些时间的数量级并不感到惊讶,因为我在很多物体上使用蛮力方法(O(n^2))。但我想知道为什么确定性算法会有这样的差异?如

我有一个Java程序来计算n体问题。在每次迭代中,它检查每个物体对其他物体施加的力,然后根据力移动它们

物体总是从同一个点开始(我把它们排列成一个圆圈,从物体0到物体n),它们总是按照相同的顺序进行检查和移动(从物体0到n)。然而,当我运行程序30次时,我得到的运行时间却截然不同。一个运行时间为2947188毫秒(49分钟),另一个运行时间为920967毫秒(15分钟)。我对这些时间的数量级并不感到惊讶,因为我在很多物体上使用蛮力方法(O(n^2))。但我想知道为什么确定性算法会有这样的差异?如果一次又一次地使用相同的算法,那么运行时间是否应该相同(或者至少相近)

在你问之前,是的,我在测量进行计算的线程的时间,而不是挂钟时间

编辑-我这样测量时间:

ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long startUserTimeNano = bean.getCurrentThreadCpuTime();

// ... Code to do the stuff...

double taskUserTimeNano = (bean.getCurrentThreadCpuTime() - startUserTimeNano);
CPUmillisecondsElapsed += taskUserTimeNano/1000000.0;
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long startUserTimeNano = bean.getCurrentThreadUserTime();

// ... Code to do the stuff...

double taskUserTimeNano = (bean.getCurrentThreadUserTime() - startUserTimeNano);
CPUmillisecondsElapsed += taskUserTimeNano/1000000.0;
除了计算步骤之外,这是否度量了其他内容

第二次编辑-现在我将其更改为这样测量时间:

ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long startUserTimeNano = bean.getCurrentThreadCpuTime();

// ... Code to do the stuff...

double taskUserTimeNano = (bean.getCurrentThreadCpuTime() - startUserTimeNano);
CPUmillisecondsElapsed += taskUserTimeNano/1000000.0;
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long startUserTimeNano = bean.getCurrentThreadUserTime();

// ... Code to do the stuff...

double taskUserTimeNano = (bean.getCurrentThreadUserTime() - startUserTimeNano);
CPUmillisecondsElapsed += taskUserTimeNano/1000000.0;
然而,结果仍然不可重复。我还试着用flag-Xint运行我的程序,结果仍然不可重复


假设问题存在于算法和多线程中是否安全?或者这仍然是与Java有关的问题吗?

您确定您正确地测量了时间吗?jvm中可能有许多线程对用户不可见。如果您得到了系统时间,那么您将忽略这些线程的存在。此外,如果您正在运行任何其他线程,您可能会忽略它们加载的时间。

由于动态编译、垃圾收集和自适应优化,对多线程程序进行基准测试可能具有挑战性。我建议阅读Brian Goetz的《Java并发性的实践》第12章。假设正在计时的线程只是众多线程中的一个,简单地获取线程中计算的开始时间和结束时间的差值实际上并不是测量线程实际占用的CPU时间。它与其他线程竞争,在其他线程运行时必须挂起/等待,但您也要计算它等待的时间

由于线程调度的方式可能有所不同,而且我们还必须考虑垃圾收集,线程从开始到结束所花费的总时间可以变化很多。但实际所需的CPU时间不会改变

编辑

我认为您需要衡量的是线程在用户模式下花费的时间。这应该消除等待OS/JVM所花费的时间,并且只测量在线程中运行的时间。因此,尝试使用
getCurrentThreadUserTime


我发现了一个关于它的早期SO线程:

您运行的条件是否会发生变化?请尝试JProfiler的评估副本?-你可以连接到你的程序。保存运行快照、查看内存、cpu利用率等。一个预感可能是内存和垃圾收集是你的问题。你说你只是在给一个线程计时。还有其他线程吗?那么我如何严格衡量执行计算的线程的时间呢?关于Java并发性的好书!是的,如果他在HotSpot VM上运行,我会说这是由编译器引起的。不久前,我回答了一个类似的问题: