如何使用精确控制的执行时间启动Java程序?

Asa*_*iev 21 java execution-time

正如我在启动Java应用程序之前所知道的那样,它为它分配了一些RAM,并且这个内存可以在启动之前由用户控制.但是当我启动应用程序时,每次都有不同的执行时间,还有另一个问题.

这是一个非常简单的for循环示例:

package timespent;

public class Main {

    public static void main(String[] args) {

        long startTime = System.nanoTime();

        int j = 0;

        for (int i = 0; i < 1.E6; i++) {
            j = i + 1;
        }

        long endTime = System.nanoTime();

        long duration = (endTime - startTime);

        System.out.println("duration = " + duration);
    }
}
Run Code Online (Sandbox Code Playgroud)

它打印出不同的结果:

duration = 5720542
duration = 7331307
duration = 7737946
duration = 6899173
Run Code Online (Sandbox Code Playgroud)

假设我希望它能够精确地执行10,000,000纳秒或10毫秒.

我想要什么?

我希望java应用程序执行精确的时间执行.

我为什么需要这个?

当我启动一个应用程序时,我想在加载所有组件之前显示应用程序启动窗口上剩下的确切执行时间.

我想这是一种CPU操作,我想知道它是否可能.

Q1:在Java中有可能吗?
Q2:如果在Java中不可能,那么有没有办法通过访问OS本机方法来实现这一点.例如,通过优先考虑Java应用程序或其他东西?
问题3:如何将应用程序的状态保存到文件并将其加载到内存中?

cma*_*ter 40

您的时间测量存在许多不确定性来源.所有这些来源不仅影响您的测量,而且运行时本身也是不确定的.不确定性的来源包括:

  • 缓存使用(内存的哪些部分缓存在CPU中).CPU执行后台任务可以从缓存中清除您的数据.

  • 内存放置(直接连接到正在执行的CPU内核的内存?).这可能会随着时间的推移而发生变化,因为您的流程可能会随时迁移到其他核心.

  • 软件中断(您的操作系统抢占您的进程以运行不同的操作).在安静的机器上运行可以稍微减轻,但不能保证你不会被打断.

  • 热量限制(您的CPU决定它太热并调低其时钟速度).除非你准备在一些具有固定时钟速度的嵌入式处理器上工作,否则你无能为力.

  • 硬件中断(您的网络连接器从Internet上的另一台计算机接收到一些数据).无论什么时候,你都没有影响.

  • 不可预测的延迟(您正在从磁盘读取一些数据,但首先,磁盘必须等到数据到达读取头下方).当您一遍又一遍地重复完全相同的操作时,这可能会遵循模式,但是一旦得到不相关的硬件中断,这可能会导致意外的延迟1/7200 rpm * 60 s/min = 8.3 ms.

  • 垃圾收集(您询问Java,因此您在后台运行GC).即便是最好的,最现代化的垃圾收集者也不能完全避免不时地停止这个世界.即使他们没有停止世界,他们仍然在后台运行,通过缓存,内存放置和软件中断引入运行时噪声.

这些可能是最重要的来源,也可能有其他来源.关键是,您的过程永远不会在机器上独自存在.除非你在没有操作系统的情况下运行并禁用所有硬件中断,否则你必须忍受这样一个事实:你的运行时间因执行而异,并且根本无法解决这个问题.

  • @cmaster:你没有提到一个很大的问题:_CPU speed_.对于像OP这样的紧密循环,CPU速度是一个关键因素.而这本身就受到_temperature_的影响,这本身就是不可预测的. (2认同)

Jer*_*der 16

这根本不可能.首先,以纳秒为单位的测量时间并不准确.我觉得这篇文章解释得很好.

其次,您无法控制CPU如何计划执行.可能还有其他任务占用CPU时间,这会延迟程序的执行.

  • @EroriCube无论你如何衡量它,最终你需要一个硬实时的操作系统来实现这一目标.您可以在几秒钟内测量并且通常是正确的,但在某些时候,您的系统会开始对驱动器进行碎片整理,并且您错过最后期限> 1秒. (3认同)

Boh*_*ian 9

任意代码的精确执行时间是非确定性的,因为它取决于物理机器同时执行的其他操作.

即使你计划通过跟踪启动时间戳和计划的结束时间戳并使主线程在退出程序之间的持续时间内使执行时间"恒定",它仍然会变化,这是一个相当大的数量.

线程执行或等待的时间和持续时间超出了程序员的控制范围.


MT0*_*MT0 6

[TL; DR]这是非常困难/不可能的.

更长的答案见路径加法的平面测试博士论文 - 基准方法论章节的一些问题,包括:

  1. 其他应用程序有资源.即没有足够的内存,操作系统必须分页应用程序; 或者代码运行较慢,因为另一个应用程序正在共享CPU.
  2. 时钟分辨率 - 您的代码只能以您的CPU能力运行.将它移植到另一台计算机上,您的基准测试可能会给您带来截然不同的结果,因此不要仅为您的系统进行优化.
  3. 类加载 - 当JVM首次运行代码时,它必须将类加载到内存中(通常来自磁盘)并解析字节代码,以便第一次运行时将比后续时间慢得多.
  4. 即时编译 - 当JVM首次加载字节代码时,它将以纯解释模式运行它.一旦它运行了一个字节代码块(即代码中的一个函数)多次(比如10,000),它就可以将该函数编译为本机代码.编译将减慢执行速度,但后续调用将更快,因为它运行本机代码而不是解释代码.但是,这不是一次性编译,如果JVM发现块中的某些执行路径受到青睐,那么它可能会重新编译字节代码以尝试优化这些路径,这可能会导致字节的多个本机版本代码,直到JVM稳定代码以防止其统计信息.
  5. 垃圾收集 - 有时您的代码将在Java调用垃圾收集器时被中断.

因此,如果您想对应用程序进行基准测试,以了解它如何以最佳方式运行:

  • 尽可能多地停止其他应用程序;
  • 运行代码数万次;
  • 忽略前10,000 - 20,000次执行(以减轻类加载和JIT编译); 和
  • 发生垃圾收集时忽略迭代(这比听起来更难确定).

它会让你了解最佳性能,但最佳和真实世界是两个截然不同的东西.


Mat*_*nry 5

接近这一点的唯一方法是在专门用于支持实时执行的操作系统上使用实时Java.