4

This question is not about benchmark.

I have a java thread cycle that should to operate close to a period time T:

public class MyRunnable implements Runnable {

    private final long period = 10000L; //period T, in this case 10 secs

    public void run() {
        while() {
            long startTime = this.getTime();

            this.doStuff();

            long endTime = this.getTime();
            long executionTime = endTime - startTime;
            if(executionTime < this.period) {
                long sleepTime = (this.period - executionTime);
                try {
                     Thread.sleep(sleepTime);
                } catch(InterruptedException iex) {/*handle iex*/}
            }
        }
    }

    private long getTime() {
        return System.currentTimeMillis();
    }

    private void doStuff() {/*do stuff*/}

} 

Of course, depending on the schedule premption choices, Thread.sleep(sleepTime) could be slightly greater than sleepTime. But, in average, this approach provides a close averaged approximation to the period T.

The problem

The method:

private long getTime() {
    return System.currentTimeMillis();
}

provides the wall clock reference time. If the machine's clock changes, forward or backwards, this implementation fails to provide a close approximation for the period T. For instance:

long t1 = getTime();
Thread.sleep(30000);
long t2 = getTime();
System.out.println(t2 - t1);

prints something like 204283 if someone manually changes the clock three minutes ahead while Thread.sleep(30000) "runs".

Since the system clock is always changing (timeserver synchronization, system load, user settings, etc...) using System.currentTimeMillis() is not realiable enough for my needs.

Failed solution

In order to provide a more robust time reference I tried the following implementation for getTime method:

long getTime() {
    long result;
    ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
    if (mxBean.isThreadCpuTimeSupported()) {
        result = mxBean.getCurrentThreadCpuTime()/1000000L;
    } else {
        throw new RuntimeException("unsupported thread cpu time");
    }
    return result;
}

The problem of getCurrentThreadCpuTime is that the resulting amount of time is the CPU time consumed by the Thread, not the time left at that instant. Time left when the Thread is sleeping or blocked is not taken in account. For example:

long t1 = getTime();
Thread.sleep(30000);
long t2 = getTime();
System.out.println(t2 - t1);

surprising prints "0" (zero) by the getCurrentThreadCpuTime getTime's implementation.

What I want

I guess that what I need is something like this:

private long getTime() {
    long cpuCycles = getAmountOfCPUCyclesSinceTheProgramStarted();
    long cpuFrequency = getCPUFrequency();
    long result = cpuCycles / cpuFrequency;
    return result;
}

The problem is that I didn't found a java way to implement getAmountOfCPUCyclesSinceTheProgramStarted() and getCPUFrequency() in a cross platform fashion.

Finally, my question is: how to obtain the spent time of a method in java in a reliable and cross-platform way?

Duloren
  • 1,455
  • 1
  • 16
  • 24
  • 1
    @Jim Garrison Please read the question again. There is no reference to a benchmark here. The solutions in the question that you tagged are useless here – Duloren Dec 28 '17 at 19:44
  • You are asking how to calculate the CPU time used by a method. That time will change as the JIT compiler optimizes the code but that doesn't happen immediately. You seem to be under the impression that the CPU time used by a method is a constant, which is not true. The best you can do is a statistical average that may change significantly from execution to execution and from day to day depending on myriad factors that are not under your control. The idea that there exists a consistent _"spent time of a method in java in a reliable and cross-platform way"_ is incorrect. – Jim Garrison Dec 28 '17 at 19:47
  • If your question is a roundabout way to ask about high-precision scheduling then you should be aware that Java is not a real-time scheduling capable framework. There is a JSR (JSR001) for a real-time Java specification but no implementation that I could find. – Jim Garrison Dec 28 '17 at 19:51
  • Please read the question again. There's no assumption about method constant time. The question isn't about benchmarking nothing. I guess you didn't understood what I need. I need compensate my application in real time in order to obtain a periodic task. The time of each execution of the method is what I need. – Duloren Dec 28 '17 at 19:51
  • 4
    Am I missing something or is `System.nanoTime()` what you are looking for? From [the docs:](https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#nanoTime--) **This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time.** – markspace Dec 28 '17 at 19:51
  • Have you tried just using a Timer with a given cycle? It should give you a pretty good approximation, and it's straightforward. – Kayaman Dec 28 '17 at 19:51
  • 1
    Sorry, the post is an [XY Problem](http://xyproblem.info). Your actual goal is realtime scheduling, which has nothing to do with the amount of CPU time used in a method. I suggest you rewrite the post to ask specifically about realtime scheduling instead. – Jim Garrison Dec 28 '17 at 19:54
  • 1
    @markspace thank you ```System.nanoTime()``` seems to be what I need. – Duloren Dec 28 '17 at 19:59
  • @markspace could you answer the question in order I can accept it as correct? – Duloren Dec 28 '17 at 20:22
  • 1
    Sure, I've added an answer now. – markspace Dec 28 '17 at 20:37

1 Answers1

4

Try using System.nanoTime(), it seems to be what you are looking for.

From the docs:

This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time.

Basil Bourque
  • 218,480
  • 72
  • 657
  • 915
markspace
  • 9,246
  • 2
  • 20
  • 35
  • FYI, `System.nanoTime()` seems to be a count of the **time since machine booted** (Java 9, Oracle JVM, macOS Sierra). Try running: `TimeUnit.MINUTES.convert(System.nanoTime(), TimeUnit.NANOSECONDS) ` As the doc mentions, this means you will have a problem when reaching the limit of a 64-bit integer in 292 years if your computer is still running then uninterrupted. ;-) – Basil Bourque Dec 29 '17 at 20:58
  • This is all in the docs but as an added note: "time since (virtual) machine booted" means you can't use `System.nanoTime()` to exchange times between virtual machines, even ones running on the same physical machine. Each JVM initializes its own counter when it starts and the counters are not related between different JVMs. – markspace Dec 29 '17 at 21:57
  • No, in my trials **the count is since the host OS** booted, not when the JVM booted. I can quit my IDE, verify no `java` process exists, then restart IntelliJ and rerun code to get 85 minutes throughout, having restarted my Mac an hour and a half ago. – Basil Bourque Dec 29 '17 at 22:12
  • Hmm, interesting. The docs say: "[this method works in] an instance of a Java virtual machine; other virtual machine instances are likely to use a different origin." So that I believe just be happenstance of the current implementation on your OS. I don't think that behavior is guaranteed. – markspace Dec 30 '17 at 01:45
  • @BasilBourque the origin is intentionally unspecified and the method’s specification explicitly states that it “*can only be used to measure elapsed time*”, which works by calculating the difference between two results of that method. This difference is entirely unrelated to the origin. Even if an overflow of the counter happens right between these two measured values, the difference will still be correct. Only if the difference itself is more than 292 years, you may get into problems. Though, if you know which value was measured first, you can treat the difference as unsigned (up to 584 yrs)… – Holger Jan 19 '18 at 08:42