1

I was running this bit of code to compare performance of 3 equivalent methods of calculating wraparound coordinates:

public class Test {
    private static final float MAX = 1000000;

    public static void main(String[] args) {
        long time = System.currentTimeMillis();
        for (float i = -MAX; i <= MAX; i++) {
            for (float j = 100; j < 10000; j++) {
                method1(i, j);
                //method2(i, j);
                //method3(i, j);
            }
        }
        System.out.println(System.currentTimeMillis() - time);
    }

    private static float method1(float value, float max) {
        value %= max + 1;
        return (value < 0) ? value + max : value;
    }

    private static float method2(float value, float max) {
        value %= max + 1;
        if (value < 0)
            value += max;
        return value;
    }

    private static float method3(float value, float max) {
        return ((value % max) + max) % max;
    }
}

I ran this code three times with method1, method2, and method3 (one method per test).

What was peculiar was that method1 spawned several java processes, and managed to utilize nearly 100% of my dual core cpu, while method2 and method3 spawned only 1 process and therefore could only take up 25% of my cpu (since there are 4 virtual cores). Why would this happen?

Here are the details of my machine:

Java 1.6.0_65

Macbook Air 13" late 2013

mleyfman
  • 635
  • 4
  • 13
  • 4
    You are not spawning any new threads here. It will not magically do this for you either. If you are seeing multiple cpus being used, either your GC has gone mad, or your cpu monitoring is incorrect. – Peter Lawrey Jul 26 '14 at 19:04
  • 1
    I suspect it's the GC. The difference won't be in the ternary operator - it will be between `+=` and `+`, since the former doesn't require space for a new value and the latter does. – Dawood ibn Kareem Jul 26 '14 at 19:06
  • I think you meant threads, rather than processes. I second the GC hypothesis, but I would like to know how you noted the threads being spawned. Not that relevant, but I ran it on my box and got more or less the same time for all of them, with the third being a bit slower. Also, remember that having multiple threads does not necessarily result in more than one core being used. – Stefano Sanfilippo Jul 26 '14 at 19:09
  • I opened up Activity monitor and saw lots of java processes running (for method1), with cpu usage maxed out for the first, and at 25% for the other 2. Also, I wrote processes because that's how it appears in the Activity monitor (under column process name). I am not sure if these are actually threads or not. – mleyfman Jul 26 '14 at 19:10
  • Post on image of the activity monitor. How are you running your program? – Sotirios Delimanolis Jul 26 '14 at 19:16
  • I am running this code under eclipse. Interestingly enough, I cannot seem to replicate the results anymore and the completion time is much faster (around 2 minutes) than when I ran the tests initially. – mleyfman Jul 26 '14 at 19:25
  • I assume by GC you mean garbage collector. There shouldn't be any GC going on here to speak of. I suspect it's some problem with using float (double is more likely to be compatible with the native FPU), making the method arguments non-final, modifying one of the passed values, and using the + operator inside the terniary expression. The JRE shouldn't go nuts here, but this particular combination may be throwing the interpreter on this particular JRE a curveball it can't handle. – Lin Sherman Jul 26 '14 at 19:27
  • 1
    Based on the description so far, my guess would neither be GC nor any other program-related overhead. It sounds like you simply (accidentally) started your program several times, by clicking the "Run"-button while the previous run was not yet finished (because, as you said, it will take "20 minutes to finish") ... voting to close this one. – Marco13 Jul 26 '14 at 19:43
  • 2
    Please read about microbenchmarks. Your code does not have any side effects, so the hot spot compiler may kick in any time and decide that there is nothing to compute amyway. My far fetched guess about the many threads would be compiler threads. To make sure the compiler does not optimize everything away, just total the output of your methods and finally print it. – Harald Jul 26 '14 at 19:46
  • @Marco13, I did not run multiple instances simultaneously. – mleyfman Jul 26 '14 at 19:48
  • Perhaps Eclipse just happened to pick that exact moment to do its own garbage collection. Perhaps something else you were running did too. These things happen. In general, if you're benchmarking anything, measure it several times, not just once. – Dawood ibn Kareem Jul 27 '14 at 02:12

1 Answers1

1

After reviewing the various comments and failing at consistently replicating the behavior. I have decided that this is indeed a fluke of the JRE. Thanks to the comments, I have learned that benchmarking this way is not very useful and I should use a Micro-benchmarking framework such as Google's caliper to test such minute differences.

Some resources I found:

  1. Google's java micro-benchmark page
  2. IBM page discussing a flawed benchmark and common pitfalls
  3. Oracle's page on micro-benchmarking
  4. Stackoverflow question on micro-benchmarking
Community
  • 1
  • 1
mleyfman
  • 635
  • 4
  • 13
  • Just a site note: it might not be relevant anyway. And even if you have a scenario where it IS relevant then the context is important: what happens before and after to the JIT can inline it or optimize it. If you use a good benchmarking framework (JMH) it might be better to synthetically test the performance of the single condition, but it might not be the same in your actual code (and not relevant to its performance). – eckes Jul 26 '14 at 20:05