1

I am attempting to model slot machine behavior with parallel programming, using a base class (SlotBase) which will be overridden by classes representing each individual machine.

In the base class, I have a method that (attempts to) invoke a descendant class's method (marked abstract in the base class) that plays a sample and returns results through use of output parameters, and then using a mutually exclusive lock, updates the base class's summation variables.

Relevant code samples as follows: From SlotBase.cs:

#region Member Variables
protected long m_CoinIn;
protected long[] m_CoinOut;
protected string[] m_FeatureList;
protected long[] m_HitCount;
// .. and many more, but redacted for length
#endregion


protected abstract void PlaySample(long sampleSize, out long coinIn, out long[] coinOut, out long[] hitCount);

protected override void DoSimulation() {
    // No need to intialize these, as the calling routine will, and only it knows how big the arrays need to be, and so forth.
    object mutex = new object();

    if (ParallelMode) {
        int periodCount = (int)(BatchSize / PeriodSize);
        Parallel.For(0, periodCount, delegate(int i) {
            long coinIn;
            long[] coinOut;
            long[] hitCount;

            PlaySample(PeriodSize, out coinIn, out coinOut, out hitCount);
            lock (mutex) {
                Console.WriteLine("Coin in this batch: {0}", coinIn);
                m_CoinIn += coinIn;

                for (int j = 0; j < m_FeatureList.Length; ++j) {
                    m_CoinOut[j] += coinOut[j];
                    m_HitCount[j] += hitCount[j];
                }
            }
        });
    }
}

.. and from a typical subclass implementation:

protected override void PlaySample(long sampleSize, out long coinIn, out long[] coinOut, out long[] hitCount) {
    switch (WagerIndex) {
        case (int)WagerType.Main: {
            RNG localRNG = SpawnRNG();
            coinIn = 0;
            coinOut = new long[m_FeatureList.Length];
            hitCount = new long[m_FeatureList.Length];


            for (long iter = 0; iter < sampleSize; ++iter) {
                coinIn += m_LinesPlayed;
                double[] output = GetSpinResults(ref localRNG, (int)SpinMode.MainSpin);

                for (int i = 0; i < m_FeatureList.Length; ++i) {
                    coinOut[i] += (long)output[i];
                    if (output[i] > 0) ++hitCount[i];
                }
            }                   
            break;
        }
        default: {
            throw new Exception(string.Format("Wager Type index {0} not supported", WagerIndex));
        }
    }
}

.. this actually works quite effectively with small values of SampleSize and PeriodSize but quickly proceeds to hang (or virtually hang) as values increase.

I've tried commenting out the variable updates and the hanging continues in earnest, which suggests that the problem is in fact with the way I'm implementing the Parallel.For loop.

I have no problems tearing this down pretty much from scratch and rebuilding to get this working. My only really important design goal in that I have the same paradigm (one base class, multiple sub-classes for implementation of different slots).

Where am I going wrong?

Dave New
  • 38,496
  • 59
  • 215
  • 394
Charles
  • 577
  • 2
  • 7
  • 16
  • 1
    This [question](http://stackoverflow.com/questions/6977218/parallel-foreach-can-cause-a-out-of-memory-exception-if-working-with-a-enumera/6977656#6977656) may help - I can't tell if your process is CPU bound from the code above, because it isn't obvious what `SpawnRNG` and `GetSpinResults` are doing. I would play with `ParallelOptions.MaximumDegreeOfParallelism` and see what happens. – davisoa Nov 27 '12 at 21:49
  • **small values** _small_ is very relative. What are the numbers we talk about? And do you care about the hangs or the performance itself? – igrimpe Nov 27 '12 at 22:54
  • I care about the hangs and the performance because the values are small relative to the amount of iterations I need to run. To give exact values, this runs five with PeriodSize = 1000 and BatchSize = 100000; change that to 2k/200k and it starts to lag a bit near the end of the runs; 4k/400k and it hangs permanently. – Charles Nov 27 '12 at 22:57
  • 1
    Does it continue to hang if you comment out the Console.WriteLine statement within the lock? Also note that without a max degree of || limit, the scheduler will spawn new loops until CPU is maxed out, so if one thread locks it will just spawn another. I've run into real problems when there is locking on a resource because of this. Try to implement without the locking (thread-safe additions) or limit the max degree of ||. – John Nov 28 '12 at 02:10

0 Answers0