Sure, I realize how difficult it is to assess these things, especially sight 
unseen.  I was mostly fishing to see if there was a known issue with the 
concurrency API or anything like that.

But let me answer your questions and I'll post the part that invokes 
concurrency as well.

At the moment I don't have access to a Linux box, but I'll see if I can get 
this stuff run on one.

Not synchronizing on anything.  When I posted the output, I was in fact using 
Math.random() API, which, according to the JDK Javadocs, can synchronize, so I 
switched to using java.util.Random.nextInt(int) off a separate Random object, 
which is what the Javadoc tells you to do if you're running in multiple 
threads.  That actually hurt the single-threaded run by a few seconds and 
didn't seem to affect the concurrent mode.

I've run it in both server and client mode with not perceptible difference.  
I've also experimented with different heap allocations, which seems mostly to 
affect the concurrent mode by causing it to run out of heap if not given enough 
memory.  Otherwise, there doesn't seem to be much of a difference.

As to the number of threads in concurrent mode, I've experimented with 
different setups, but for now I've settled on running each player as separate 
Future objects, so that makes 3 threads in concurrent mode, and each of those 
spawns up to separate Future objects for each game play, limited to 1000 
threads in concurrent mode.

Here's the interesting part:

package zinger.goats;

import java.util.*;
import java.util.concurrent.*;

public class Play
{
        public static class Tester implements Callable<String>
        {
                protected final Player player;
                protected final int tries;
                protected final ExecutorService executor;
                
                public Tester(final Player player, final int tries, final 
ExecutorService executor)
                {
                        this.player = player;
                        this.tries = tries;
                        this.executor = executor;
                }
                
                public String call()
                {
                        return Play.testPlayer(this.player, this.tries, 
this.executor);
                }
        }

        public static void main(final String... args)
        {
                final long startMS = System.currentTimeMillis();
                final int tries = Integer.parseInt(args[0]);
                final int maxThreads = args.length > 1 ? 
Integer.parseInt(args[1]) : 1;
                final ExecutorService testExecutor = maxThreads > 1 ?  
                        Executors.newFixedThreadPool(Math.min(3, maxThreads)) :
                        Executors.newSingleThreadExecutor();
                final ExecutorService gameExecutor = maxThreads > 1 ? 
                        Executors.newFixedThreadPool(Math.min(maxThreads, 
tries)) :
                        Executors.newSingleThreadExecutor();
                try
                {
                        final List<Future<String>> futures = new 
ArrayList<Future<String>>(3);
                        futures.add(testExecutor.submit(new Play.Tester(new 
RandomPlayer(), tries, gameExecutor)));
                        futures.add(testExecutor.submit(new Play.Tester(new 
NeverChangePlayer(), tries, gameExecutor)));
                        futures.add(testExecutor.submit(new Play.Tester(new 
AlwaysChangePlayer(), tries, gameExecutor)));
                        for(Future<String> future : futures)
                        {
                                try
                                {
                                        System.out.println(future.get());
                                }
                                catch(final InterruptedException ex)
                                {
                                        ex.printStackTrace();
                                }
                                catch(final ExecutionException ex)
                                {
                                        ex.printStackTrace();
                                }
                        }
                }
                finally
                {
                        System.out.println("Execution time: " + 
((System.currentTimeMillis() - startMS) / 1000L) + "sec.");
                        testExecutor.shutdown();
                        gameExecutor.shutdown();
                }
        }

        public static String testPlayer(final Player player, final int tries, 
final ExecutorService executor)
        {
                final StringBuilder sb = new StringBuilder();
                int carCount = 0;
                int goatCount = 0;
                final int[] carDistribution = new int[3];
                final int[] goatDistribution = new int[3];
                
                final List<Game> games = new ArrayList<Game>(tries);
                final List<Future<?>> futures = new ArrayList<Future<?>>(tries);
                for(int i = 0; i < tries; ++i)
                {
                        final Game game = new Game(player, new Random());
                        games.add(game);
                        futures.add(executor.submit(game));
                }
                
                for(int i = 0; i < tries; ++i)
                {
                        try
                        {
                                futures.get(i).get();
                        }
                        catch(final InterruptedException ex)
                        {
                                ex.printStackTrace();
                                continue;
                        }
                        catch(final ExecutionException ex)
                        {
                                ex.printStackTrace();
                                continue;
                        }
                        final Game game = games.get(i);
                        switch(game.getPrize())
                        {
                                case CAR:
                                ++carCount;
                                break;
                                
                                case GOAT:
                                ++goatCount;
                                break;
                        }
                        for(int doorIndex = 0; doorIndex < 
game.doorView.size(); ++doorIndex)
                        {
                                final Game.Door door = 
game.doorView.get(doorIndex);
                                switch(door.open())
                                {
                                        case CAR:
                                        ++carDistribution[doorIndex];
                                        break;
                                        
                                        case GOAT:
                                        ++goatDistribution[doorIndex];
                                        break;
                                }
                        }
                }
                
                sb.append(player.getDescription()).append(" won 
").append(carCount).append(" cars and ").append(goatCount).append(" goats.\n");
                sb.append("Cars were distributed as follows:");
                for(final int prizeCount : carDistribution)
                        sb.append('\t').append(prizeCount);
                sb.append("\nGoats were distributed as follows:");
                for(final int prizeCount : goatDistribution)
                        sb.append('\t').append(prizeCount);
                sb.append('\n');
                return sb.toString();
        }
}

And here's how it's invoked:

<?xml version="1.0"?>
<project name="Goats" default="run">
        <property name="tries" value="1000000"/>
        <property name="maxThreads" value="1000"/>
        
        <target name="init">
                <mkdir dir="${basedir}/cls"/>
        </target>
        
        <target name="compile" depends="init">
                <javac destdir="${basedir}/cls" srcdir="${basedir}/src" 
debug="true"/>
        </target>
        
        <target name="run" depends="compile">
                <echo message="Tries: ${tries}; max threads: ${maxThreads}."/>
                <echo message="Single-threaded..."/>
                <echo/>
                <java classname="zinger.goats.Play" classpath="${basedir}/cls" 
fork="true" failonerror="true">
                        <!--<jvmarg value="-Xmx1000m"/>-->
                        <jvmarg value="-server"/>
                        <arg value="${tries}"/>
                </java>
                <echo/>
                <echo message="Concurrent..."/>
                <java classname="zinger.goats.Play" classpath="${basedir}/cls" 
fork="true" failonerror="true">
                        <jvmarg value="-Xmx1000m"/>
                        <jvmarg value="-server"/>
                        <arg value="${tries}"/>
                        <arg value="${maxThreads}"/>
                </java>
        </target>
</project>

And the latest output:

Tries: 1000000; max threads: 1000.
Single-threaded...

Random Player won 499013 cars and 500987 goats.
Cars were distributed as follows:       332940  333982  333078
Goats were distributed as follows:      667060  666018  666922

Never Change Player won 333605 cars and 666395 goats.
Cars were distributed as follows:       332908  333852  333240
Goats were distributed as follows:      667092  666148  666760

Always Change Player won 665827 cars and 334173 goats.
Cars were distributed as follows:       333435  332470  334095
Goats were distributed as follows:      666565  667530  665905

Execution time: 30sec.

Concurrent...
Random Player won 500155 cars and 499845 goats.
Cars were distributed as follows:       333661  334231  332108
Goats were distributed as follows:      666339  665769  667892

Never Change Player won 333164 cars and 666836 goats.
Cars were distributed as follows:       332788  333709  333503
Goats were distributed as follows:      667212  666291  666497

Always Change Player won 666344 cars and 333656 goats.
Cars were distributed as follows:       333603  333938  332459
Goats were distributed as follows:      666397  666062  667541

Execution time: 39sec.

--- On Fri, 11/14/08, Reinier Zwitserloot <[EMAIL PROTECTED]> wrote:

> From: Reinier Zwitserloot <[EMAIL PROTECTED]>
> Subject: [The Java Posse] Re: single-threaded vs concurrent performance on 
> dual-core
> To: "The Java Posse" <[email protected]>
> Date: Friday, November 14, 2008, 3:03 PM
> There are a gazillion factors at work here, from bugs in
> your code to
> JVM implementation to kernel thread scheduler. Without
> exquisite
> detail, I don't think its even possible to shed some
> light as to why
> you're seeing a slight (less than doubling is
> 'slight', in meaningful
> profiling). Even with all that info, I doubt -I- could tell
> you,
> though I cannot of course speak for other posse listeners.
> 
> A few quick and dirty ones:
> 
>  - try this on linux 2.6. It has a kick ass thread
> scheduler.
>  - are you synchronizing on anything, or did you add
> Thread.sleep/wait
> anywhere? That tends to make stuff worse.
>  - are you on the server VM? I don't think this matters
> anymore in
> 6u10, though, as the mixed mode VM is pretty smart, or so I
> hear.
>  - are you using exactly 2 threads, slightly more than
> that, or a
> massive amount of them?
> 
> 
> On Nov 14, 7:04 pm, Alexey Zinger
> <[EMAIL PROTECTED]> wrote:
> > A little while ago I wrote a simulation of the Monty
> Hall puzzle.  The simulator is given 3 players of different
> strategies (one makes a decision at random, the other never
> switches doors, and the third always switches) and runs each
> player through a configurable number of games, outputting
> each player's outcomes at the end.  Seems fairly simple
> and everything was working as expected.
> >
> > Seeing as I have a dual core machine (XP Pro, JRE
> 1.6_10), I looked at the way the CPU was being utilized and
> to my surprise I saw a fairly even utilization of both
> cores, despite the program being single-threaded.  Then I
> decided to make the simulator multi-threaded and see what
> would happen to the utilization graph and speed of the
> application.  I converted the
> >  code invoking the game generation and running API to
> use Java concurrency framework, which also game me good
> control over how many threads I wanted to use or if I wanted
> to run everything single-threaded still.  Once I got
> everything working again, I was dismayed to see somewhat
> uneven invocation of the cores and, more importantly, the
> multi-threaded mode seems to be running consistently slower
> than the single-threaded mode.  It also uses more memory,
> which appears to be directly proportional to how many
> threads I allow it to allocate.  The memory demands make
> sense, but what's the deal with worse performance in
> concurrent mode?  Here is a sample output:
> >
> > Tries: 1000000.
> > Single-threaded...
> > Random Player won 500177 cars and 499823 goats.
> > Cars were distributed as follows:       332820 
> 333805  333375
> > Goats were distributed as follows:      667180 
> 666195  666625
> >
> > Never Change Player won 333555 cars and 666445 goats.
> > Cars were distributed as follows:       333274 
> 333294  333432
> > Goats were distributed as follows:      666726 
> 666706  666568
> >
> > Always Change Player won 667476 cars and 332524 goats.
> > Cars were distributed as follows:       332660 
> 333696  333644
> > Goats were distributed as follows:      667340 
> 666304  666356
> >
> > Execution time: 28sec.
> >
> > Concurrent...
> > Random Player won 499379 cars and 500621 goats.
> > Cars were distributed as follows:       333939 
> 333019  333042
> > Goats were distributed as follows:      666061 
> 666981  666958
> >
> > Never Change Player won 333384 cars and 666616 goats.
> > Cars were distributed as follows:       333502 
> 331961  334537
> > Goats were distributed as follows:      666498 
> 668039  665463
> >
> > Always Change Player won 666691 cars and 333309 goats.
> > Cars were distributed as follows:       334080 
> 332585  333335
> > Goats were distributed as follows:      665920 
> 667415  666665
> >
> > Execution time: 38sec.
> >
> > I'm using a separate random number generator for
> each game play, so that at least shouldn't be the bottle
> neck according to the Javadocs.  I'd be happy to post
> the source if need be, but I'm hoping I'm missing
> something simple.
> >
> > Alexey
> > 2001 Honda CBR600F4i (CCS)
> > 1992 Kawasaki
> EX500http://azinger.blogspot.comhttp://bsheet.sourceforge.nethttp://wcollage.sourceforge.net
> 

      

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "The 
Java Posse" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/javaposse?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to