5

I'm making a sort of probability simulator that will run for either a certain amount of time, or for a certain amount of repetitions. I'm looking to optimize it, and it's currently multi-threaded with each ProbabilityWorker extending Thread, and the main program will automatically allocate n threads, where n is however many threads are available (example: on my Core i3-7100U, this is 4).

I'm analyzing the performance of this, and I'm realizing that the methodology I'm using to get the current time with respect to the end time is causing a lot of overhead.

For the mode where it can "run for a certain amount of time," I was making new Date objects as a part of the loop condition, then I changed it to a faster System.currentTimeMillis() to try and save time, but I'm noticing even that induces overhead.

My run function looks like this:

public void run() {
    if (mode) {
        while (completed < repitions) {
            resultSet[randy.nextInt(o)]++;
            completed++;
        }
    } else {
        while (System.currentTimeMillis() < endTime) {
            resultSet[randy.nextInt(o)]++;
            completed++;
        }
    }
    done = true;
}

Where mode is true if running for an amount of repetitions, randy is a Random, o is the amount of possible outcomes, and endTime is the end point in milliseconds, system time (which could be modified, program takes in an amount of seconds, and endTime is calculated by the current time plus secondsInput * 1000).

In addition, on the same Core i3-7100U, these are my performance statistics:

DE-WEY-LAPTOP:/mnt/c/Users/danny/Documents/Programming/Data Structures/Probability$ java Main -n 10000000000

Running 10000000000 repitions of the probability simulator with 2 possible outcomes.
4 threads detected on system; doing 2500000000 repitions per thread.
Done. Gathering results from worker threads...
Done. Printing results...
Outcome 1: 4999997330 out of 10000000000 (49.9999733%)
Outcome 2: 5000002670 out of 10000000000 (50.0000267%)
Time taken: 43.443 seconds (2.301866813986143E8 ops/sec)

DE-WEY-LAPTOP:/mnt/c/Users/danny/Documents/Programming/Data Structures/Probability$ java Main -t 44

Running the probability simulator for 44 seconds using 4 threads.
Done. Gathering results from worker threads...
Done. Printing results...
Outcome 1: 141568074 out of 283130850 (50.000935609807264%)
Outcome 2: 141562776 out of 283130850 (49.999064390192736%)
Time taken: 44 seconds (6434792.045454546 ops/sec)

My question is, is there a way to optimize the System.currentTimeMillis() call to either not have it or reduce how much time it takes? Is there another faster call I can use?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
DDPWNAGE
  • 1,423
  • 10
  • 37
  • 4
    `System.nanoTime()` is better suited for your purpose (timing how long something takes). Also see [Time measuring overhead in Java](https://stackoverflow.com/questions/5640409/time-measuring-overhead-in-java). – Mick Mnemonic Feb 15 '18 at 15:49
  • 3
    You can do a check how many operations are done in one second and check the time any N operations. You might lose half a second here or there but it will improve ;) For example if you aim at millions you can do the timing check every 50k attempts. It will be : while ( your condition ) { for 1:50000 { do that operations } } – Veselin Davidov Feb 15 '18 at 15:51
  • You're calling `System.currentTimeMillis()` 283130850 times in the loop? I really think there is a better way - implement `tick` and chose when you want to check is the given time less than current time. – zlakad Feb 15 '18 at 16:00
  • 1
    *"I'm using to get the current time with respect to the end time is causing a lot of overhead"* - Define overhead; it is what is called a "busy loop", a loop which invokes as many times as the allotted CPU time allows. You may want to introduce a call to Thread.sleep() to make that loop a little less CPU hungry - and also reduce the amount of method calls done at the same time. – Gimby Feb 15 '18 at 16:13
  • Generating a random integer is much more expensive than currentTimeMillis(). Try to profile your code. – LMC Feb 15 '18 at 19:16

1 Answers1

3

You should really be looking into System.nanoTime (and stick to that) - that is the best you can get in the JVM AFAIK. Besides the fact that it measures elapsed time without any notion of clock time, it is the fastest too - that is the reason JMH uses it (or any other sane micro-benchmark I hope).

Besides the fact that System.currentTimeMillis returns ms precision (and there are things that are done faster than 1ms), the difference between two calls to this method can return a negative value.

There are two things to keep in mind though, first is that each call to System.nanoTime has a performance implication as well, on average it takes (on my, close to your CPU and JVM-9) 25 ns per call.

The last point is that System.nanoTime has nano-second precision, but not nano-second accuracy. This means that when you call:

long start = System.nanoTime();
long end = System.nanoTime();

both of the results will return a number that has nano-second precision, that is they will have multiple digits.

But that number can not be very accurate. Well, accurate according to what you might ask. Since System.nanoTime returns a value that is arbitrary, there isn't something to compare it with, unless other call to System.nanoTime, thus:

long result = end - start;

is/might not going to be a nano-second accurate result. The inaccuracy of this is around 1 micro-second, on my laptop is around 0.2-0.5 micro-seconds.


Use System.nanoTime, there isn't something faster or more fine-grained.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • Okay, except I want to make the program run for a certain amount of time, that is, “n” seconds. – DDPWNAGE Feb 15 '18 at 21:42
  • In addition, implementing `System.nanoTime()` rather than `System.getCurrentTimeMillis()` is actually much slower in this instance -- I can get 6,438,567 ops/second by using `System.getCurrentTimeMillis()`, whereas `System.nanoTime()` gets just 1,771 ops/second. – DDPWNAGE Feb 15 '18 at 22:20
  • @DDPWNAGE I *just wonder* (irony face inserted here) if that is your code doing something fishy, vs `nanoTime` and `getCurrentTimeMillis` – Eugene Feb 16 '18 at 08:03
  • I tried this myself also and `nanoTime()` is actually slower than `currentTimeMillis()`. The reason is explained [here](https://stackoverflow.com/questions/19052316/why-is-system-nanotime-way-slower-in-performance-than-system-currenttimemill). In my system the difference is not that large though; `currentTimeMillis()` is maybe 3 times faster. – Mick Mnemonic Feb 16 '18 at 11:39
  • @MickMnemonic indeed! but 3 times faster is quite A LOT, I am around 5ns difference running via JMH https://justpaste.it/1h6d2 – Eugene Feb 16 '18 at 11:56
  • 1
    Apparently the difference depends quite a lot on the OS; I am using Windows where `currentTimeMillis()` is (apparently) fast. – Mick Mnemonic Feb 16 '18 at 11:58
  • I’m running Windows myself, @Mnemonic. Eugene , I posted the entire loop - I’m using time as a break condition. – DDPWNAGE Feb 16 '18 at 13:05