Why is execution times measured differently here using Java? - java

What is the difference between these 2? They both give me the execution-times with slighlty different values and I can tell they are written differently. But since the outcome is nearly identical, what does code 2 do that code 1 does not? What actually is the function of "sum" and is it even executed?
Code 1:
for (int i = 0; i < 10; i++)
{
long n0 = System.nanoTime();
long n1 = System.nanoTime();
System.out.println(" resolution " + (n1 - n0) + " nanoseconds");
}
Code 2:
int[] given = {1,2,3,4,5,6,7,8,9,0};
int sum = 0;
for (int i = 0; i < 10; i++)
{
long t0 = System.nanoTime();
sum += given[i];
long t1 = System.nanoTime();
System.out.println(" resolution " + (t1 - t0) + " nanoseconds");
}
Here is the output for those who asked:
Code 1:
Code 2:

It is simply code to try out System.nanoTime(). Doing something or nothing between two calls does not make a discernible difference. The resolution of the clock is about 100 ns.
As sum is not printed, the compiler might have optimized the code by removing just that extra code.
Furthermore it seems that nanoTime alone already requires ~100 ns.
Note
The code is primarily written for ones own curiosity.

Related

Weird Multithreading Performance of Simple Benchmark (Java)

Recently I wrote a really simple code as a benchmark to see performance increase in my machine. It just creates some number of threads and divides some number of spins between those threads. Below is my class which extends Thread in java:
public static class LoopThread extends Thread {
int index;
long numberOfRound;
long numberOfSpins;
public LoopThread(int index, long numberOfRound, long numberOfSpins) {
this.index = index;
this.numberOfRound = numberOfRound;
this.numberOfSpins = numberOfSpins;
}
public void run() {
System.out.println("Thread " + index + " started for " + numberOfRound + " rounds and " + numberOfSpins + " spins");
for(long i = 0; i < numberOfRound; i++) {
for(long j = 0; j < numberOfSpins; j++) {
}
}
System.out.println("Thread " + index + " ended");
}
The weird thing is when I first wrote this piece of code, it was almost scaling linearly until 8 threads. However, it was taking more time with 9 threads and the increase was small after 9 threads although my machine has 16 hardware threads. In order to investigate the problem, I just changed the code in a simple way so I put a time information in the last line of the code like:
System.out.println("Thread " + index + " ended: " + System.currentTimeMillis());
This change suprisingly made my code more scalable. Since I can't see an obvious reason behind why this change caused this improvement, I tried again and again and here some results which I consistently see:
Before the change:
time passed for 8 threads and 1000000 rounds and 800000 spins is 63 seconds
time passed for 9 threads and 1000000 rounds and 800000 spins is 69 seconds
After the simple change:
time passed for 8 threads and 1000000 rounds and 800000 spins is 62 seconds
time passed for 9 threads and 1000000 rounds and 800000 spins is 56 seconds
Again, I can't see any obvious reason behind that and it seems very weird to me. Do you have any idea about why this is happening?
Thanks
Edit(the code which starts threads and times):
public static void main(String[] args) {
int numberOfThreads = Integer.parseInt(args[0]);
long numberOfRounds = Long.parseLong(args[1]);
long numberOfSpins = Long.parseLong(args[2]);
long startTime = System.currentTimeMillis();
Thread[] threads = new Thread[numberOfThreads];
for( int i = 0; i < numberOfThreads; i++) {
threads[i] = new LoopThread(i, numberOfRounds, (long) numberOfSpins/numberOfThreads);
threads[i].start();
}
for(Thread t: threads) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long endTime = System.currentTimeMillis() - startTime;
System.out.println("time passed for " + numberOfThreads + " threads and " + numberOfRounds + " rounds and " + numberOfSpins + " spins is " + endTime/1000 + " seconds");
}

Java Strange Float Behavior

Here is what my code looks like stripped down as much as possible:
float delay = (float)5000;
long startTime = System.nanoTime();
int elapsed = 0;
for (int i = 0; i < 20; i++) {
elapsed = (int) ((System.nanoTime() - startTime) / 1000000);
// System.out.println("Elapsed: " + elapsed);
float range = delay * 0.4f;
float randomNum = (float)(Math.random() * range - (delay * 0.2f));
if (elapsed > (delay + randomNum)) {
System.out.println("Random Num: " + randomNum);
startTime = System.nanoTime();
} else {
i--;
continue;
}
}
As you can see I'm looping 20 times and printing out a random number after 5 seconds (5000 milliseconds). This is what the output looks like:
As you can see, all of the outputs are VERY close to -1000. I'm trying to generate a random float from -1000 to 1000, but they all seem to be around -1000. So, I checked to make sure the actual random number generator is working, using this code:
float delay = (float)5000;
long startTime = System.nanoTime();
int elapsed = 0;
for (int i = 0; i < 20; i++) {
elapsed = (int) ((System.nanoTime() - startTime) / 1000000);
// System.out.println("Elapsed: " + elapsed);
float range = delay * 0.4f;
float randomNum = (float)(Math.random() * range - (delay * 0.2f));
System.out.println("Random Num: " + randomNum);
startTime = System.nanoTime();
}
Basically I took elapsed out of the equation and just printed the random numbers without the if statement. This is the output I got:
In this example, I got very random output, exactly like you would expect. However, once you add elapsed back into the equation like in the first set of code, the output turns back to -999 with some random decimal places.
Even more interesting is that if you put a print statement right above the if statement and right after randomNum is assigned a value, you get these outputs:
Once again, the numbers are random. It seems like for some reason in the first example code with the elapsed part of the code put in, the randomNum variable changes right after the if statement is called. Why does this happen?
The problem is that, although you're generating random numbers in your desired range, you're systematically discarding all the ones over -999.
Consider code like this:
while (true) {
// generate a random number in the range [-1000, 1000):
final double randomNum = 2000 * Math.random() - 1000;
// print it if it's in the range [-1000, -999):
if (randomNum < -999) {
System.out.println("Random Num: " + randomNum);
}
}
The above code will print out a bunch of random numbers in the range [−1000, −999); do you see why?
Your code is more complicated, of course, but it's effectively doing the same thing.
To see why, let's take a look at your code:
float delay = (float)5000;
long startTime = System.nanoTime();
int elapsed = 0;
for (int i = 0; i < 20; i++) {
elapsed = (int) ((System.nanoTime() - startTime) / 1000000);
// System.out.println("Elapsed: " + elapsed);
float range = delay * 0.4f;
float randomNum = (float)(Math.random() * range - (delay * 0.2f));
if (elapsed > (delay + randomNum)) {
System.out.println("Random Num: " + randomNum);
startTime = System.nanoTime();
} else {
i--;
continue;
}
}
Let's simplify/trim it a bit so it's easier to read — remove the commented-out line, clean up the whitespace, remove the casts to int and float (it's OK to use long and double), inline the various values, change the for-loop-that-contains-code-that-mutates-its-index-variable into a more-explicit while-loop, change the System.nanoTime()-but-then-dividing-the-result-by-a-million to System.currentTimeMillis(), rename some variables for clarity, etc.:
long prevTimeMillis = System.currentTimeMillis();
int i = 0;
while (i < 20) {
final long elapsedMillis = System.currentTimeMillis() - prevTimeMillis;
final double randomNum = 2000 * Math.random() - 1000;
if (elapsedMillis > 5000 + randomNum) {
System.out.println("Random Num: " + randomNum);
prevTimeMillis = System.currentTimeMillis();
i++;
}
}
Even with that simpler code in hand, we still need two key insights:
elapsedMillis > 5000 + randomNum is just another way of writing randomNum < elapsedMillis - 5000.
Initially, elapsedMillis == 0; and after each time we successfully print out a number, elapsedMillis == 0 again. In between, there are some loop iterations where elapsedMillis increases by 1, but in most loop iterations it doesn't change at all.
This is because this loop is very very quick, with a very large number of iterations per millisecond. (That's not necessarily obvious from first principles, but it's the only way to explain the output you're getting.)
So this code will loop quickly, generating one random number after another and discarding every single one, until elapsedMillis == 4001, at which point every random number will be discarded except random numbers less than -999. Since you're performing a huge number of loop iterations per millisecond, and generating a huge number of random numbers per millisecond, it's overwhelmingly likely that you manage to generate a random number less than -999 while elapsedMillis == 4001. And then elapsedMillis gets reset back to zero. So random numbers greater than -999 never have a chance to compete: elapsedMillis is never greater than 4001, so such numbers are always discarded.
To fix this, you need to pre-select a single random number "How long should I delay?" before you start looping, and then loop until your elapsedMillis exceeds that one pre-selected random number.
In addition, assuming that your real goal here is to delay for some amount of time in the range [4sec,6sec), you should probably use Thread.sleep() rather than this polling/busy-waiting mechanism. That way, instead of just burning CPU, you can gracefully yield this processor for use by other threads and processes until you're ready to proceed. To do that, you can write:
for (int i = 0; i < 20; i++) {
final long millisToDelay = (long) (2000 * Math.random() - 1000);
System.out.println("Millis To Delay: " + millisToDelay);
Thread.sleep(millisToDelay);
}
I believe you actually have the answer to your problem located on the second line of code you have provided to us. Notice how you cast to float: (float)?
Do the same thing on the line that contains:
float number = delay + randomNum;
so that it looks like:
float number = (float) delay + randomNum;
delay is not a float data type. I think that should do it.

java, timing three different loops

While reading essential java item45, there are three different simple loops as shown in the code below with timing. The first and the third is preferred. But when I time them,
for N < 1,000,000:
length of the list: 1000000
sum is: 499999500000
fast method time: 25
sum is: 499999500000
slower method time: 5
sum is: 499999500000
range method time: 21
the second method is actually faster, I think this may because java compiler is smart enough to substitute the a.size() with the actual number.
However, when N grows, the second method indeed becomes slower. I am aware this experiment is naive and machine specific. I wonder if there is an explanation for the second method outperform the other two when N is small. (the program have been run multiple times)
length of the list: 10000000
sum is: 49999995000000
fast method time: 44
sum is: 49999995000000
slower method time: 48
sum is: 49999995000000
range method time: 37
The code:
public static void main(String [] args){
// test the speed of initialize 1 million elements
// timing two different loops.
// int N = 10000000;
int N = 1000000;
List<Integer> a = new ArrayList<Integer>();
for(int i = 0; i < N; ++i){
a.add(i);
}
System.out.println("length of the list: " + a.size());
long t1 = System.currentTimeMillis();
long sum = 0;
for(int i = 0, n = a.size(); i < n; ++i){
sum += a.get(i);
}
long t2 = System.currentTimeMillis();
System.out.println("sum is: " + sum);
System.out.println("fast method time: " + (t2 - t1));
t1 = System.currentTimeMillis();
sum = 0;
for(int i = 0; i < a.size(); ++i){
sum += a.get(i);
}
t2 = System.currentTimeMillis();
System.out.println("sum is: " + sum);
System.out.println("slower method time: " + (t2 - t1));
t1 = System.currentTimeMillis();
sum = 0;
for(int i: a){
sum += i;
}
t2 = System.currentTimeMillis();
System.out.println("sum is: " + sum);
System.out.println("range method time: " + (t2 - t1));
}
I was indeed having the same results than you:
length of the list: 1000000
sum is: 499999500000
fast method time: 32
sum is: 499999500000
slower method time: 12
sum is: 499999500000
range method time: 24
So I used javap -c to dissasemble the bytecode, and I saw that javac was not making any kind of optimization when seeing that N was small, and in deed, no optimization was being done.
So, I tried exchanging the order of the first two statements, and here is the result:
length of the list: 1000000
sum is: 499999500000
slower method time: 30
sum is: 499999500000
fast method time: 8
sum is: 499999500000
range method time: 25
So the difference between those two methods is not the method itself, but which one happens first will be the slower.
As for why it happens, it is still puzzling me (maybe deferred loading of some class? hot-code native compilation?)

With java jdk 1.7 64-bit, a for loop using an int is 20+ times faster than a for loop with a long. Why?

See edits below
There is no casting going on with the termination check. I would think the < and the ++ would be as fast with ints and longs on a 64bit machine. But I guess not?
int: 65 milliseconds:
public void testWTF() throws Exception {
int runs = 10;
long hs = 0;
long timeSum = 0;
for (int run = 0; run < runs; run++) {
int term = Integer.MAX_VALUE;
long start = System.currentTimeMillis();
// ***** loop to be tested ******
for (int i = 0; i < term; i++) {
hs++;
}
timeSum += (System.currentTimeMillis() - start);
System.out.println("hs = " + hs);
hs = 0;
}
System.out.println("timeSum = " + timeSum);
System.out.println("avg time = " + (timeSum / runs) + " for " + runs + " runs");
System.out.println("hs = " + hs);
}
long: 1445 milliseconds
public void testWTF() throws Exception {
int runs = 10;
long hs = 0;
long timeSum = 0;
for (int run = 0; run < runs; run++) {
long term = Integer.MAX_VALUE;
long start = System.currentTimeMillis();
// ***** loop to be tested ******
for (long i = 0; i < term; i++) {
hs++;
}
timeSum += (System.currentTimeMillis() - start);
System.out.println("hs = " + hs);
hs = 0;
}
System.out.println("timeSum = " + timeSum);
System.out.println("avg time = " + (timeSum / runs) + " for " + runs + " runs");
System.out.println("hs = " + hs);
}
hardware: 64-bit Xeon running windows 7 64bit.
edit: I updated this to do several iterations. For 1 million runs with the int version, the average time is 65 milliseconds. The long version takes too long for 1 million, 1000 and even 100. For 10 runs the average time is 1447 milliseconds.
Also, I'm using hs outside the loop so that the loop does not get jitted away.
This is a very bad/unreliable/unrealistic way of doing benchmarks, since the JIT isn't really given a chance to do much optimization -- you only run the benchmarks once, and you measure the first run.
Basically, Java's JIT will optimize your code significantly more once it sees your code getting used extensively. In a real program, the JIT will be optimizing any critical loops, so if you want a benchmark that mimics the real world, you have to convince the JIT to kick in.
The simplest way to get an accurate benchmark in Java is to use a tool like Caliper that knows how to properly warm up the JIT and get accurate measurements, and then see if the results are more consistent.

Java - Big result difference when benchmarking an algorithm in ms and ns

I was performing some test performance on an algorithm and noticed something weird. Maybe I am missing something here.
I first measure the time in milliseconde:
long startTime = System.currentTimeMillis();
x.sort(sortStringInput);
long endTime = System.currentTimeMillis();
and then in nanoseconde:
long startTime = System.nanoTime();
x.sort(sortStringInput);
long endTime = System.nanoTime();
The results are 437ms qnd 26366ns.
I am calling the same method so how can it be possible to get a result in ns which is way smaller than the one in ms. I know that 1 ms is 1 000 000 ns so 26366 is even smaller than 1 ms...
Thanks,
Are you sorting the same list twice? The second call will be extremely fast if the list is already sorted.
Depending on what platform you're on, System.nanoTime() itself can be very slow. You're better off running your benchmark multiple times and measuring the overall duration in miliseconds.
I suggest you run the test for at least 2 seconds before counting any result and run the test for at least 2 seconds and take the average. The following code prints.
Average sort time 116 ms.
Average sort time 117100526 ns.
Average sort time 116 ms.
Average sort time 116530255 ns.
Average sort time 117 ms.
Average sort time 116905977 ns.
Code
public static void main(String... args) throws IOException {
String[] strings = new String[100 * 1000];
for (int i = 0; i < strings.length; i++)
strings[i] = "" + Math.random();
int runTimeMS = 2000;
for (int i = 0; i <= 3; i++) {
{
long start = System.currentTimeMillis();
int count = 0;
do {
Arrays.sort(strings.clone());
count++;
} while (System.currentTimeMillis() - start < runTimeMS);
long time = System.currentTimeMillis() - start;
if (i>0) System.out.println("Average sort time " + time / count + " ms.");
}
{
long start = System.nanoTime();
int count = 0;
do {
Arrays.sort(strings.clone());
count++;
} while (System.nanoTime() - start < runTimeMS * 1000L * 1000L);
long time = System.nanoTime() - start;
if (i>0) System.out.println("Average sort time " + time / count + " ns.");
}
}
}

Categories

Resources