0

I'm working on a simple benchmark testing out both Mono's ParallelFX against Java on several Linux boxes. The test for .NET runs great on Windows and Linux alike, but I'm having some kind of snag with the Java version...

I can see the specified number of threads starting up, but they run in a strange fashion. It acts like they start up, but they finish very slowly. They continue to start, but take forever to finish. It seems like it should be exceeding the limit of the thread pool, and my CPU usage looks to me like it's only using one or two cores (I've got an i7 processor so something like 8 should try to be used).

Yes, I know I am not being "thread safe" with my integers and probably other stuff too. I don't really care right now. Something larger is an issue here.

C# Version

public class Program
{
    static void Main(string[] args)
    {
        const int numberOfCycles = 1000;
        const int numbersPerCycle = 1000000;

        Stopwatch swG = Stopwatch.StartNew();

        int threadCount = 0;
        int completeCount = 0;
        Parallel.For(0, numberOfCycles, x =>
            {
                Console.WriteLine(string.Format("Starting cycle {0}. Thread count at {1}", x, threadCount++));

                Random r = new Random();
                Stopwatch sw = Stopwatch.StartNew();
                List<double> numbers = new List<double>();
                for (int i = 0; i < numbersPerCycle; i++)
                {
                    numbers.Add(r.NextDouble() * 1000);
                }
                numbers.Sort();
                double min = numbers.Min();
                double max = numbers.Max();

                completeCount++;
                Console.WriteLine(string.Format("{0} cycles complete: {1:#,##0.0} ms. Min: {2:0.###}  Max: {3:0.###}", completeCount, sw.ElapsedMilliseconds, min, max));
                threadCount--;
            });

        Console.WriteLine(string.Format("All {0} cycles complete. Took {1:#,##0.0} ms.", numberOfCycles, swG.ElapsedMilliseconds));
        Console.WriteLine("Press any key to continue.");
        Console.ReadKey();
    }
}

Java Version

P.S. I am lazy and stole the Stopwatch class from here: Is there a stopwatch in Java?

public class JavaMonoTest {

    static int threadCount = 0;
    static int completeCount = 0;
    static String CLRF = "\r\n";

    public static void main(String[] args) throws IOException, InterruptedException {
        final int numberOfCycles = 1000;
        final int numbersPerCycle = 1000000;
        final int NUM_CORES = Runtime.getRuntime().availableProcessors();

        //Setup the running array
        List<Integer> cyclesList = new LinkedList<Integer>();
        for(int i = 0; i < numberOfCycles; i++){
            cyclesList.add(i);
        }

        Stopwatch swG = new Stopwatch();
        swG.start();

       ExecutorService exec = Executors.newFixedThreadPool(NUM_CORES);
       try {
           for (final Integer x : cyclesList) {
               exec.submit(new Runnable() {
                   @Override
                   public void run() {
                       System.out.printf("Starting cycle %s. Thread count at %s %s", x, threadCount++, CLRF);

                       Random r = new Random();
                       Stopwatch sw = new Stopwatch();
                       sw.start();
                       List<Double> numbers = new LinkedList<Double>();
                       for (int i = 0; i < numbersPerCycle; i++)
                       {
                           numbers.add(r.nextDouble() * 1000);
                       }
                       Collections.sort(numbers);
                       double min = Collections.min(numbers);
                       double max = Collections.max(numbers);

                       completeCount++;
                       System.out.printf("%s cycles complete: %.2f ms. Min: %.2f  Max: %.2f %s", completeCount, sw.getElapsedTime(), min, max, CLRF);
                       threadCount--;
                   }
               });
           }
       } finally {
           exec.shutdown();
       }
       exec.awaitTermination(1, TimeUnit.DAYS);

        System.out.printf("All %s cycles complete. Took %.2f ms. %s", numberOfCycles, swG.getElapsedTime(), CLRF);
        System.out.println("Press any key to continue.");
        System.in.read();
    }
}

Updated C# Version to Match Java Version In Answer

public class Program
{
    static void Main(string[] args)
    {
        const int numberOfCycles = 1000;
        const int numbersPerCycle = 1000000;

        Stopwatch swG = Stopwatch.StartNew();

        int threadCount = 0;
        int completeCount = 0;
        Parallel.For(0, numberOfCycles, x =>
            {
                Console.WriteLine(string.Format("Starting cycle {0}. Thread count at {1}", x, Interlocked.Increment(ref threadCount)));

                Random r = new Random();
                Stopwatch sw = Stopwatch.StartNew();
                double[] numbers = new double[numbersPerCycle];
                for (int i = 0; i < numbersPerCycle; i++)
                {
                    numbers[i] = r.NextDouble() * 1000;
                }
                Array.Sort(numbers);
                double min = numbers[0];
                double max = numbers[numbers.Length - 1];

                Interlocked.Increment(ref completeCount);
                Console.WriteLine(string.Format("{0} cycles complete: {1:#,##0.0} ms. Min: {2:0.###}  Max: {3:0.###}", completeCount, sw.ElapsedMilliseconds, min, max));
                Interlocked.Decrement(ref threadCount);
            });

        Console.WriteLine(string.Format("All {0} cycles complete. Took {1:#,##0.0} ms.", numberOfCycles, swG.ElapsedMilliseconds));
        Console.WriteLine("Press any key to continue.");
        Console.ReadKey();
    }
}
Community
  • 1
  • 1
jocull
  • 20,008
  • 22
  • 105
  • 149
  • Have you ever considered also trying running the java code under Mono with IKVM.net? I was curious about comparing those two under this condition myself. – Daniel Monteiro Oct 22 '12 at 18:09
  • I hadn't considered doing that. Seems like extra hoops to jump when you can get native JVMs fairly easily? If you do try it out, let us know what you find! – jocull Oct 23 '12 at 03:25
  • 1
    Sorry. Tried really hard, but couldn't get it to run under Mono. I actually do that all the time with my code. I have a level editor the uses some classes from my game engine. The engine is Java Android and the editor is made within MonoDevelop, using GTK#. http://ataquedascuruja.files.wordpress.com/2012/06/screenshot-monobed-home-monty-projects-bzk3-source-leveleditor-monobed3-monobed3-bin-debug-level0-level.png – Daniel Monteiro Oct 27 '12 at 13:34

2 Answers2

2

Running the program I see that its using 97%-98% of eight cpus, but also creating an insane amount of garbage. If I make the program more efficient it runs to completion much faster.

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class JavaMonoTest {

    static final AtomicInteger threadCount = new AtomicInteger();
    static final AtomicInteger completeCount = new AtomicInteger();

    public static void main(String[] args) throws InterruptedException {
        final int numberOfCycles = 1000;
        final int numbersPerCycle = 1000000;
        final int NUM_CORES = Runtime.getRuntime().availableProcessors();

        long swG = System.nanoTime();

        ExecutorService exec = Executors.newFixedThreadPool(NUM_CORES);
        try {
            for (int i = 0; i < numberOfCycles; i++) {
                final int x = i;
                exec.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                        System.out.printf("Starting cycle %s. Thread count at %s %n", x, threadCount.getAndIncrement());

                        Random r = new Random();
                        long sw = System.nanoTime();
                        double[] numbers = new double[numbersPerCycle];
                        for (int i = 0; i < numbersPerCycle; i++) {
                            numbers[i] = r.nextDouble() * 1000;
                        }
                        Arrays.sort(numbers);
                        double min = numbers[0];
                        double max = numbers[numbers.length - 1];

                        completeCount.getAndIncrement();
                        System.out.printf("%s cycles complete: %.2f ms. Min: %.2f  Max: %.2f %n",
                                completeCount, (System.nanoTime() - sw) / 1e6, min, max);
                        threadCount.getAndDecrement();
                        } catch (Throwable t) {
                            t.printStackTrace();
                        }
                    }
                });
            }
        } finally {
            exec.shutdown();
        }
        exec.awaitTermination(1, TimeUnit.DAYS);

        System.out.printf("All %s cycles complete. Took %.2f ms. %n",
                numberOfCycles, (System.nanoTime() - swG) / 1e6);
    }
}

prints

Starting cycle 0. Thread count at 0 
Starting cycle 7. Thread count at 7 
Starting cycle 6. Thread count at 6 
   ... deleted ...
999 cycles complete: 139.28 ms. Min: 0.00  Max: 1000.00 
1000 cycles complete: 139.05 ms. Min: 0.00  Max: 1000.00 
All 1000 cycles complete. Took 19431.14 ms. 
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • You must be on one hell of a machine, mine still took 40+ seconds. Well done regardless - I guess Java does not handle garbage quite as gracefully as .NET? I was hoping for a more 1:1 comparison, so I will rewrite the .NET app accordingly. Thanks! – jocull Oct 22 '12 at 07:52
  • 2
    I suspect your .NET List is equivalent to a Java's ArrayList. LinkedList is fast but uses much more memory which is a problem if you don't have much. Its a Dual Xeon 3 GHz with 32 GB of memory but usually I use a much bigger machine with 256 GB but its busy doing work ;) – Peter Lawrey Oct 22 '12 at 07:55
  • That is probably part of the problem - I'm not much of a Java coder, I just know enough to get into trouble... – jocull Oct 22 '12 at 07:57
  • I find it best to compare efficient code for benchmarking purposes. If you compare inefficient code you can get strange result like you can write a test which shows Java is much faster than C, but that is because Java has some dead code optimisations that C doesn't. – Peter Lawrey Oct 22 '12 at 07:58
  • The updated .NET version seems to run in about 60 some seconds on my machine, so now Java is winning... I'll put the updated code above. – jocull Oct 22 '12 at 08:05
0

In place of:

   ExecutorService exec = Executors.newFixedThreadPool(NUM_CORES);
   try {
       for (final Integer x : cyclesList) {
           exec.submit(new Runnable() {

try:

   ExecutorService exec = Executors.newFixedThreadPool(NUM_CORES);
   try {
       for (final Integer x : cyclesList) {
           exec.execute( new Runnable() {    // No Future< T > needed
Aubin
  • 14,617
  • 9
  • 61
  • 84
  • This seems to run better, but something is still blocking the threads I think. CPU usage will spike, but still stays relatively low. I also got a few `java.lang.OutOfMemoryError: Java heap space` errors on `numbers.add(r.nextDouble() * 1000);` - this did not happen before. – jocull Oct 22 '12 at 07:20
  • Running with -Xmx1G passed seems to fix the heap errors but the CPU is still not staying as active as I would like. – jocull Oct 22 '12 at 07:25