Does entity name length impact program performance in case of reflection? - java

in the case of using reflection we are accessing entities by their names encoded in strings like this m = getMethod("someMethod"). To find the requested entity a string comparison has to be done. Does it mean that the length of the entity name influences the performance. If it so how much is this impact on the performance?

The answer is heavily dependent on the Java Virtual Machine, you're using. I wrote a test program, just to get some numbers for a JVM 1.8.0_05 (yes, it's old ;-):
import java.lang.reflect.Method;
public class ReflectionAccessTest {
public final static void main(String[] args) throws Exception {
for (int i = 0; i < 100000; i++) {
// do some "training"
ReflectionTarget.class.getMethod("a", Integer.TYPE, Integer.TYPE);
ReflectionTarget.class.getMethod("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Integer.TYPE, Integer.TYPE);
ReflectionTarget.class.getMethod("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Integer.TYPE, Integer.TYPE);
}
Method method = null;;
long start;
start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
// do some "training"
method = ReflectionTarget.class.getMethod("a", Integer.TYPE, Integer.TYPE);
}
System.out.println("Time to get method with short name " + (System.currentTimeMillis() - start) + " ms");
start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
method.invoke(null, Integer.MAX_VALUE, Integer.MIN_VALUE);
}
System.out.println("Time to execute method with short name " + (System.currentTimeMillis() - start) + " ms");
start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
// do some "training"
method = ReflectionTarget.class.getMethod("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Integer.TYPE, Integer.TYPE);
}
System.out.println("Time to get method with medium name " + (System.currentTimeMillis() - start) + " ms");
start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
method.invoke(null, Integer.MAX_VALUE, Integer.MIN_VALUE);
}
System.out.println("Time to execute method with medium name " + (System.currentTimeMillis() - start) + " ms");
start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
// do some "training"
method = ReflectionTarget.class.getMethod("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Integer.TYPE, Integer.TYPE);
}
System.out.println("Time to get method with long name " + (System.currentTimeMillis() - start) + " ms");
start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
method.invoke(null, Integer.MAX_VALUE, Integer.MIN_VALUE);
}
System.out.println("Time to execute method with long name " + (System.currentTimeMillis() - start) + " ms");
}
private static class ReflectionTarget {
public static void a(int a, int b) {
// do nothing
}
public static void aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(int a, int b) {
// do nothing
}
public static void aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(int a, int b) {
// do nothing
}
}
}
The output is as follows:
Time to get method with short name 1012 ms
Time to execute method with short name 58 ms
Time to get method with medium name 3690 ms
Time to execute method with medium name 177 ms
Time to get method with long name 6279 ms
Time to execute method with long name 180 ms
The times are actually dependent on the length of the name (that surprised me first but on second thought it's obvious because there needs to be some kind of equaliy-test that is length-dependent).
But you can also see that the impact is negligible. A call of getMethod takes 0.1 nanoseconds for a method with a name with only one character and takes 0.6 nanoseconds for a method with a crazy long name (haven't counted the number of as).
If this difference is actually relevant for you, you might try out caching-mechanisms of the method you retrieved. But dependent on the time the called method takes, that might be completely useless unless its execution time is also in the range of sub-nanoseconds.

Related

Learning to avoid using static methods/variables. having trouble making a non-static method that returns a number

I want to try practicing avoiding using static methods/variables when not needed, because I've heard/seen/been told that you want to avoid using them when you can. I decided to make a simple password cracker in Java:
import java.util.Random;
public class PasswordCracker
{
public static void main(String args[])
{
PasswordCracker pwcSimulation = new PasswordCracker();
long totalTimeSpentCracking = 0;
int numSimulations = 100;
for(int i = 0; i < numSimulations; i++)
{
System.out.println(pwcSimulation.PasswordCrackingSimulation());
}
}
long PasswordCrackingSimulation()
{
long startTime = System.currentTimeMillis();
int upperBound = 999999;
Random rand = new Random();
int randomPassword = rand.nextInt(upperBound);
int passwordGuess;
for(int i = 0; i <= upperBound; i++)
{
passwordGuess = i;
if(passwordGuess == randomPassword)
{
System.out.println("password Guessed correctly, the password was: " + randomPassword);
break;
}
/*else
{
System.out.println("Your inputted password is incorrect, please try again.");
}*/
}
long endTime = System.currentTimeMillis();
long timeSpentCracking = (endTime - startTime);
System.out.println("The program took " + timeSpentCracking + "ms OR ~" + ((timeSpentCracking/1000) % 60) + " seconds to complete");
return timeSpentCracking;
}
}
first instantiated a new class (hopefully i did this they way you should?) to avoid having to use a static method for the method PasswordCrackingSimulation. Now i'm having trouble returning a value from the method. The printline in the loop will always print 0, so I know that it isn't taking the returned value in the method. Any help would be lovely :) just trying to learn
No, you're doing everything correctly.
You're returning how long it takes in milliseconds to crack that password.
The answer is less than 1 millisecond. That 0 you see? That's because your method is returning 0. It is doing that because endTime - startTime is zero.
Just write return 1 to test this out yourself - you'll see your print loop print 1 instead.

Wrapped types autoboxing vs "new" CODE TEST

I know from theory that creating a new wrapped number type should be made using autoboxing or factory methods, instead of using new because of the caching feature.
So i made a little test program just to experience but the results are unexpeted.
Here is the code:
package javaTest;
public class TestWrapper {
public static final int NUM_TESTS = 50000000;
public static final int SAMPLE_NUMBER = 100;
public static void main(String[] args) {
// TODO Auto-generated method stub
long time = 0;
System.out.println("Testing with " + NUM_TESTS + " iterations:");
System.out.println("\nTesting Double");
TestType[] types = TestType.values();
for(int i=0; i<types.length; i++) {
time = 0;
for(int j=0; j<SAMPLE_NUMBER; j++) {
time += testDouble(types[i]);
}
System.out.println( "\t" + types[i].toString("Double") + " mean execution time: " + (((double)time)/SAMPLE_NUMBER) + "ms");
}
System.out.println("\nTesting Integer");
for(int i=0; i<types.length; i++) {
time = 0;
for(int j=0; j<SAMPLE_NUMBER; j++) {
time += testInteger(types[i]);
//System.out.println(j);
}
System.out.println("\t" + types[i].toString("Integer") + " mean execution time: " + (((double)time)/SAMPLE_NUMBER) + "ms");
}
}
public static long testInteger(TestType type) {
Integer test;
long startTime=0;
long endTime=0;
switch (type) {
case NEW:
startTime = System.currentTimeMillis();
for (int i = 0; i < NUM_TESTS; i++) {
test = new Integer(0);
}
endTime = System.currentTimeMillis();
break;
case VALUEOF:
startTime = System.currentTimeMillis();
for (int i = 0; i < NUM_TESTS; i++) {
test = Integer.valueOf(0);
}
endTime = System.currentTimeMillis();
break;
case AUTOBOXING:
startTime = System.currentTimeMillis();
for (int i = 0; i < NUM_TESTS; i++) {
test = 0;
}
endTime = System.currentTimeMillis();
break;
}
return (endTime - startTime);
}
public static long testDouble(TestType type) {
Double test;
long startTime=0;
long endTime=0;
switch (type) {
case NEW:
startTime = System.currentTimeMillis();
for (int i = 0; i < NUM_TESTS; i++) {
test = new Double(0);
}
endTime = System.currentTimeMillis();
break;
case VALUEOF:
startTime = System.currentTimeMillis();
for (int i = 0; i < NUM_TESTS; i++) {
test = Double.valueOf(0);
}
endTime = System.currentTimeMillis();
break;
case AUTOBOXING:
startTime = System.currentTimeMillis();
for (int i = 0; i < NUM_TESTS; i++) {
test = .0;
}
endTime = System.currentTimeMillis();
break;
}
return (endTime - startTime);
}
private enum TestType {
NEW, VALUEOF, AUTOBOXING;
public String toString(String type) {
String ret = new String();
switch(this) {
case NEW:
ret = "new " + type + "(0)";
break;
case VALUEOF:
ret = type + ".valueOf(0)";
break;
case AUTOBOXING:
ret = type + " a = 0";
break;
}
return ret;
}
}
}
And the output is:
(Compiled and ran with jdk1.6.0_23)
Testing with 50000000 iterations:
Testing Double
new Double(0) mean execution time: 20.18ms
Double.valueOf(0) mean execution time: 210.18ms
Double a = .0 mean execution time: 207.73ms
Testing Integer
new Integer(0) mean execution time: 20.08ms
Integer.valueOf(0) mean execution time: 160.64ms
Integer a = .0 mean execution time: 160.77ms
As I said, i was expecting the first test to be the slowest, but the output says the opposite!
Why is this happening?
UPDATE
(Compiled and ran with jdk1.7.0_79)
Testing with 50000000 iterations:
Testing Double new Double(0) mean execution time: 1.5ms
Double.valueOf(0) mean execution time: 213.86ms Double a = 0
mean execution time: 214.29ms
Testing Integer new Integer(0) mean execution time: 1.3ms
Integer.valueOf(0) mean execution time: 141.55ms Integer a = 0
mean execution time: 141.38ms
(Compiled and ran with jdk1.8.0_121)
Testing with 50000000 iterations:
Testing Double new Double(0) mean execution time: 0.1ms
Double.valueOf(0) mean execution time: 0.0ms Double a = 0 mean
execution time: 0.0ms
Testing Integer new Integer(0) mean execution time: 0.1ms
Integer.valueOf(0) mean execution time: 0.0ms Integer a = 0 mean
execution time: 0.0ms
The same test with 100000000 iterations give this output on jdk1.8:
(Compiled and ran with jdk1.8.0_121)
Testing with 100000000 iterations:
Testing Double new Double(0) mean execution time: 54.02ms
Double.valueOf(0) mean execution time: 62.99ms Double a = 0 mean
execution time: 62.93ms
Testing Integer new Integer(0) mean execution time: 53.44ms
Integer.valueOf(0) mean execution time: 63.01ms Integer a = 0
mean execution time: 62.93ms

Getting familiar with Threads in Java: Why does this program's runtime increase with increasing number of threads

Situation
I am trying to get familiar with threads in Java. For that reason I modified a program listing I found in a book. What is does is pretty simple:
It creates a boolean[]-array with 100.000.000 elements.
It randomly fills that array's elements with true or false using NUMBER_OF_SERVERS threads.
Finally it scans that array with NUMBER_OF_SERVERS threads and counts how many entries are set to true
For further details, please see the code below on the bottom of this post.
Problem
When I run the code with different number of threads and measure the runtime, I get a very strange result; or at least a behaviour that I do not understand: The BuildService-Thread consumes MORE runtime when I use MORE threads. When building the entire array (based on random truedistribution) in just one thread that takes about 10 seconds. Next, when I use four threads I would expect the runtime to decrease. However, I get a time consumption of about 17 seconds.
My ScanService works as expected: Time consumption decreases with more threads.
Please see the following chart for details:
However, if change one line in my code and replace the if ((int) ((Math.random() * 2d)) == 0)-statement (for random true-distribution) with if (i % 2 == 0) (thus, every second item will be true) I get a behaviour I would expect:
Questions
So, my questions are:
Why do MORE threads lead to a LONGER runtime when using Math.random() function?
Vice versa, why does the runtime DECREASE when using only ONE thread using the exact same function?
What "general rules" can be derived from this behaviour when it comes down to dealing with threads?
Background info
The code was run on an Intel core i3.
Code
public class AsynchService
{
private static final int ARRAY_SIZE = 100000000; //100.000.000
private static final int NUMBER_OF_SERVERS = 16;
private static final int HOW_MANY = ARRAY_SIZE / NUMBER_OF_SERVERS;
//build array asynch
public static boolean[] buildArrayAsynch()
{
//build array with NUMBER_OF_SERVERS-Threads
boolean[] array = new boolean[ARRAY_SIZE];
Thread[] buildServerThread = new Thread[NUMBER_OF_SERVERS];
long startTime = System.currentTimeMillis();
for (int i = 0; i < NUMBER_OF_SERVERS; i++)
{
int start = i * HOW_MANY;
int end = (i != NUMBER_OF_SERVERS - 1) ? (i + 1) * HOW_MANY - 1 : ARRAY_SIZE - 1;
buildServerThread[i] = new BuildService(array, i, start, end);
}
//synchronize and wait for result
int expectedResult = 0;
for (int i = 0; i < NUMBER_OF_SERVERS; i++)
{
try
{
buildServerThread[i].join();
}
catch (InterruptedException ex) {}
expectedResult += ((BuildService) buildServerThread[i]).getExpectedResult();
}
System.out.println("\nNumber of \"true\"s ==> Expected result: " + expectedResult);
System.out.println("Build duration: " + (System.currentTimeMillis() - startTime) + " ms\n");
return array;
}
//scan array asynch
public static int scanArrayAsynch(boolean[] array)
{
//create services and server-threads
Thread[] serverThread = new Thread[NUMBER_OF_SERVERS];
long startTime = System.currentTimeMillis();
for (int i = 0; i < NUMBER_OF_SERVERS; i++)
{
int start = i * HOW_MANY;
int end = (i != NUMBER_OF_SERVERS - 1) ? (i + 1) * HOW_MANY - 1 : ARRAY_SIZE - 1;
serverThread[i] = new ScanService(array, i, start, end);
}
//synchronize with servers, wait for server end
int result = 0;
for (int i = 0; i < NUMBER_OF_SERVERS; i++)
{
try
{
serverThread[i].join();
}
catch (InterruptedException ex) {}
result += ((ScanService) serverThread[i]).getResult();
}
System.out.println("Search duration: " + (System.currentTimeMillis() - startTime) + " ms");
return result;
}
public static void main(String[] args)
{
//build array
boolean[] array = buildArrayAsynch();
//scan array
int result = scanArrayAsynch(array);
//display result
System.out.println("\nResult: " + result);
}
}
class BuildService extends Thread
{
private boolean[] array;
private int start;
private int end;
private int expectedResult = 0;
public BuildService(boolean[] array, int serviceId, int start, int end)
{
this.array = array;
this.start = start;
this.end = end;
this.setName("BuildService " + serviceId);
this.start();
}
public int getExpectedResult()
{
return expectedResult;
}
public void run()
{
if (start < 0 || end >= array.length) throw new IndexOutOfBoundsException();
System.out.println(getName() + ": StartIndex = " + start + "; EndIndex = " + end);
long startTime = System.currentTimeMillis();
for (int i = start; i <= end; i++)
{
//if (i % 2 == 0)
if ((int) ((Math.random() * 2d)) == 0)
{
array[i] = true;
expectedResult++;
}
else
{
array[i] = false;
}
}
System.out.println(getName() + " finished! \"true\" elements: " + expectedResult + "; duration = " + (System.currentTimeMillis() - startTime) + "ms");
}
}
class ScanService extends Thread
{
private boolean[] array;
private int serviceId;
private int start;
private int end;
private int result = 0;
public ScanService(boolean[] array, int serviceId, int start, int end)
{
this.array = array;
this.serviceId = serviceId;
this.start = start;
this.end = end;
this.start();
}
public int getResult()
{
return result;
}
public void run()
{
if (start < 0 || end >= array.length) throw new IndexOutOfBoundsException();
System.out.println("Server " + serviceId + ": StartIndex = " + start + "; EndIndex = " + end);
for (int i = start; i <= end; i++)
{
if (array[i]) result++;
}
}
}
The devil is in the details. The documentation of Math.random() has the answer:
This method is properly synchronized to allow correct use by more than one thread. However, if many threads need to generate pseudorandom numbers at a great rate, it may reduce contention for each thread to have its own pseudorandom-number generator.
To get around this issue, try creating an instance of java.util.Random for each instance of your BuildService class, and use that instead of Math.random(). A benefit of using java.util.Random is also that you don't have to do unnecessary double-int-arithmetic, but can simply use the nextBoolean() method.
You are probably seeing the effect of thread contention in Math.random(). Java 7 has the ThreadLocalRandom feature to avoid just this problem.
Your Code seems to be using 16 number of threads and each thread is using the Join method as in here
serverThread[i].join();
which seems to not use the full potential of threads.
when using the join you are actually saying the thread to wait till the other thread completes not running the threads in parallel.
you might want to use start method instead of join method.
Try running the changed code and post your analysis on time line.
Good luck learning
Taking Andreas Troelsen's answer into account I came up with the code shown below leading to the following runtimes.
Compared to what happened before, this solution now meets my expectations much better!
import java.util.Random;
class BuildService extends Thread
{
private boolean[] array;
private int start;
private int end;
private int expectedResult = 0;
private Random random = new Random();
public BuildService(boolean[] array, int serviceId, int start, int end)
{
this.array = array;
this.start = start;
this.end = end;
this.setName("BuildService " + serviceId);
this.start();
}
public int getExpectedResult()
{
return expectedResult;
}
public void run()
{
if (start < 0 || end >= array.length) throw new IndexOutOfBoundsException();
System.out.println(getName() + ": StartIndex = " + start + "; EndIndex = " + end);
long startTime = System.currentTimeMillis();
for (int i = start; i <= end; i++)
{
array[i] = random.nextBoolean();
if (array[i]) expectedResult++;
}
System.out.println(getName() + " finished! \"true\" elements: " + expectedResult + "; duration = " + (System.currentTimeMillis() - startTime) + "ms");
}
}

System.nanoTime vs System.currentTimeMillis

According to its documentation, System.nanoTime returns
nanoseconds since some fixed but arbitrary origin time. However, on all x64 machines I tried the code below, there were time jumps, moving that fixed origin time around. There may be some flaw in my method to acquire the correct time using an alternative method (here, currentTimeMillis). However, the main purpose of measuring relative times (durations) is negatively affected, too.
I came across this problem trying to measure latencies when comparing different queues to LMAX's Disruptor where I got very negative latencies sometimes. In those cases, start and end timestamps were created by different threads, but the latency was computed after those threads had finished.
My code here takes time using nanoTime, computes the fixed origin in currentTimeMillis time, and compares that origin between calls. And since I must ask a question here: What is wrong with this code? Why does it observe violations of the fixed origin contract? Or does it not?
import java.text.*;
/**
* test coherency between {#link System#currentTimeMillis()} and {#link System#nanoTime()}
*/
public class TimeCoherencyTest {
static final int MAX_THREADS = Math.max( 1, Runtime.getRuntime().availableProcessors() - 1);
static final long RUNTIME_NS = 1000000000L * 100;
static final long BIG_OFFSET_MS = 2;
static long startNanos;
static long firstNanoOrigin;
static {
initNanos();
}
private static void initNanos() {
long millisBefore = System.currentTimeMillis();
long millisAfter;
do {
startNanos = System.nanoTime();
millisAfter = System.currentTimeMillis();
} while ( millisAfter != millisBefore);
firstNanoOrigin = ( long) ( millisAfter - ( startNanos / 1e6));
}
static NumberFormat lnf = DecimalFormat.getNumberInstance();
static {
lnf.setMaximumFractionDigits( 3);
lnf.setGroupingUsed( true);
};
static class TimeCoherency {
long firstOrigin;
long lastOrigin;
long numMismatchToLast = 0;
long numMismatchToFirst = 0;
long numMismatchToFirstBig = 0;
long numChecks = 0;
public TimeCoherency( long firstNanoOrigin) {
firstOrigin = firstNanoOrigin;
lastOrigin = firstOrigin;
}
}
public static void main( String[] args) {
Thread[] threads = new Thread[ MAX_THREADS];
for ( int i = 0; i < MAX_THREADS; i++) {
final int fi = i;
final TimeCoherency tc = new TimeCoherency( firstNanoOrigin);
threads[ i] = new Thread() {
#Override
public void run() {
long start = getNow( tc);
long firstOrigin = tc.lastOrigin; // get the first origin for this thread
System.out.println( "Thread " + fi + " started at " + lnf.format( start) + " ns");
long nruns = 0;
while ( getNow( tc) < RUNTIME_NS) {
nruns++;
}
final long runTimeNS = getNow( tc) - start;
final long originDrift = tc.lastOrigin - firstOrigin;
nruns += 3; // account for start and end call and the one that ends the loop
final long skipped = nruns - tc.numChecks;
System.out.println( "Thread " + fi + " finished after " + lnf.format( nruns) + " runs in " + lnf.format( runTimeNS) + " ns (" + lnf.format( ( double) runTimeNS / nruns) + " ns/call) with"
+ "\n\t" + lnf.format( tc.numMismatchToFirst) + " different from first origin (" + lnf.format( 100.0 * tc.numMismatchToFirst / nruns) + "%)"
+ "\n\t" + lnf.format( tc.numMismatchToLast) + " jumps from last origin (" + lnf.format( 100.0 * tc.numMismatchToLast / nruns) + "%)"
+ "\n\t" + lnf.format( tc.numMismatchToFirstBig) + " different from first origin by more than " + BIG_OFFSET_MS + " ms"
+ " (" + lnf.format( 100.0 * tc.numMismatchToFirstBig / nruns) + "%)"
+ "\n\t" + "total drift: " + lnf.format( originDrift) + " ms, " + lnf.format( skipped) + " skipped (" + lnf.format( 100.0 * skipped / nruns) + " %)");
}};
threads[ i].start();
}
try {
for ( Thread thread : threads) {
thread.join();
}
} catch ( InterruptedException ie) {};
}
public static long getNow( TimeCoherency coherency) {
long millisBefore = System.currentTimeMillis();
long now = System.nanoTime();
if ( coherency != null) {
checkOffset( now, millisBefore, coherency);
}
return now - startNanos;
}
private static void checkOffset( long nanoTime, long millisBefore, TimeCoherency tc) {
long millisAfter = System.currentTimeMillis();
if ( millisBefore != millisAfter) {
// disregard since thread may have slept between calls
return;
}
tc.numChecks++;
long nanoMillis = ( long) ( nanoTime / 1e6);
long nanoOrigin = millisAfter - nanoMillis;
long oldOrigin = tc.lastOrigin;
if ( oldOrigin != nanoOrigin) {
tc.lastOrigin = nanoOrigin;
tc.numMismatchToLast++;
}
if ( tc.firstOrigin != nanoOrigin) {
tc.numMismatchToFirst++;
}
if ( Math.abs( tc.firstOrigin - nanoOrigin) > BIG_OFFSET_MS) {
tc.numMismatchToFirstBig ++;
}
}
}
Now I made some small changes. Basically, I bracket the nanoTime calls between two currentTimeMillis calls to see if the thread has been rescheduled (which should take more than currentTimeMillis resolution). In this case, I disregard the loop cycle. Actually, if we know that nanoTime is sufficiently fast (as on newer architectures like Ivy Bridge), we can bracket in currentTimeMillis with nanoTime.
Now the long >10ms jumps are gone. Instead, we count when we get more than 2ms away from first origin per thread. On the machines I have tested, for a runtime of 100s, there are always close to 200.000 jumps between calls. It is for those cases that I think either currentTimeMillis or nanoTime may be inaccurate.
As has been mentioned, computing a new origin each time means you are subject to error.
// ______ delay _______
// v v
long origin = (long)(System.currentTimeMillis() - System.nanoTime() / 1e6);
// ^
// truncation
If you modify your program so you also compute the origin difference, you'll find out it's very small. About 200ns average I measured which is about right for the time delay.
Using multiplication instead of division (which should be OK without overflow for another couple hundred years) you'll also find that the number of origins computed that fail the equality check is much larger, about 99%. If the reason for error is because of the time delay, they would only pass when the delay happens to be identical to the last one.
A much simpler test is to accumulate elapsed time over some number of subsequent calls to nanoTime and see if it checks out with the first and last calls:
public class SimpleTimeCoherencyTest {
public static void main(String[] args) {
final long anchorNanos = System.nanoTime();
long lastNanoTime = System.nanoTime();
long accumulatedNanos = lastNanoTime - anchorNanos;
long numCallsSinceAnchor = 1L;
for(int i = 0; i < 100; i++) {
TestRun testRun = new TestRun(accumulatedNanos, lastNanoTime);
Thread t = new Thread(testRun);
t.start();
try {
t.join();
} catch(InterruptedException ie) {}
lastNanoTime = testRun.lastNanoTime;
accumulatedNanos = testRun.accumulatedNanos;
numCallsSinceAnchor += testRun.numCallsToNanoTime;
}
System.out.println(numCallsSinceAnchor);
System.out.println(accumulatedNanos);
System.out.println(lastNanoTime - anchorNanos);
}
static class TestRun
implements Runnable {
volatile long accumulatedNanos;
volatile long lastNanoTime;
volatile long numCallsToNanoTime;
TestRun(long acc, long last) {
accumulatedNanos = acc;
lastNanoTime = last;
}
#Override
public void run() {
long lastNanos = lastNanoTime;
long currentNanos;
do {
currentNanos = System.nanoTime();
accumulatedNanos += currentNanos - lastNanos;
lastNanos = currentNanos;
numCallsToNanoTime++;
} while(currentNanos - lastNanoTime <= 100000000L);
lastNanoTime = lastNanos;
}
}
}
That test does indicate the origin is the same (or at least the error is zero-mean).
As far as I know the method System.currentTimeMillis() makes indeed sometimes jumps, dependent on the underlying OS. I have observed this behaviour myself sometimes.
So your code gives me the impression you try to get the offset between System.nanoTime() and System.currentTimeMillis() repeated times. You should rather try to observe this offset by calling System.currentTimeMillis() only once before you can say that System.nanoTimes() causes sometimes jumps.
By the way, I will not pretend that the spec (javadoc describes System.nanoTime() related to some fixed point) is always perfectly implemented. You can look on this discussion where multi-core CPUs or changes of CPU-frequencies can negatively affect the required behaviour of System.nanoTime(). But one thing is sure. System.currentTimeMillis() is far more subject to arbitrary jumps.

Testing code to get the average time for the calls

This is my code, I am trying to test what's the average time to make a call to getLocationIp method by passing ipAddress in that. So what I did is that, I am generating some random ipAddress and passing that to getLocationIp and then calculating the time difference. And then putting that to HashMap with there counts. And after wards I am priniting the hash map to see
what's the actual count. So this is the right way to test this? or there is some other way. Becuase in my case I am not sure whether my generateIPAddress method generates random ipAddress everytime. I am also having start_total time before entering the loop and then end_total time after everything gets completed. So on that I can calculate the average time?
long total = 10000;
long found = 0;
long found_country = 0;
long runs = total;
Map<Long, Long> histgram = new HashMap<Long, Long>();
try {
long start_total = System.nanoTime();
while(runs > 0) {
String ipAddress = generateIPAddress();
long start_time = System.nanoTime();
resp = GeoLocationService.getLocationIp(ipAddress);
long end_time = System.nanoTime();
long difference = (end_time - start_time)/1000000;
Long count = histgram.get(difference);
if (count != null) {
count++;
histgram.put(Long.valueOf(difference), count);
} else {
histgram.put(Long.valueOf(difference), Long.valueOf(1L));
}
runs--;
}
long end_total = System.nanoTime();
long finalTotal = (end_total - start_total)/1000000;
float avg = (float)(finalTotal) / total;
Set<Long> keys = histgram.keySet();
for (Long key : keys) {
Long value = histgram.get(key);
System.out.println("$$$GEO OPTIMIZE SVC MEASUREMENT$$$, HG data, " + key + ":" + value);
}
This is my generateIpAddress method-
private String generateIPAddress() {
Random r = new Random();
String s = r.nextInt(256) + "." + r.nextInt(256) + "." + r.nextInt(256) + "." + r.nextInt(256);
return s;
}
Any suggestions will be appreciated.
Generally when you benchmark functions you want to run the multiple times and average the results That gives you are clearer indication of the actual time your program will spend in them, considering that you rarely care about the performance of something only run once.

Categories

Resources