Thursday, May 08, 2008

 

With a basic understanding of what XUnit is doing, we need to determine where we’re going to try to split things up across multiple cores.  Take a look at the sequence diagram fom the last article (here); we have a choice to make.  It’s better to make outer loops Parallel vs. inner loops.  The design decision this helps us make  is that the unit of concurrency is the Class. This means if I make 100 Tests inside a single class, it will run sequentially just as though we had no fancy concurrency code.  In the rest of this article we’ll look at the modifications needed to use Payneallel.ForEach with the unit tests.

XUnit GUI

We start our modifications in the XUnit GUI, which is refreshingly straightforward.  The first thing to do is make it easy to choose concurrent execution.  The XUnit GUI now looks like this, following the sequential execution of the control group:

The “Run concurrently” checkbox is my addition.  When you click the Run button:

        void OnClick_Run(object sender, EventArgs e)

        {

            _totalCount = 0;

            _testCount = GetTestCount();

            ResetUI(_testCount);                                

            buttonGo.Enabled = false;

            if (_concurrentChk.Checked)

            {

                ThreadStart ts = new ThreadStart(RunAsync);

                Thread t = new Thread(ts);

                t.Name = "xUnitAsyncThread";

                t.Start();

                textResults.AppendText("Running Async...\r\n");

            }

            else

            {

                wrapper.RunAssembly(TestCallback);

            }

Our xUnit ExecutorWrapper is “wrapper”.    In order to keep from screwing around with the GUI thread, we run XUnit on a new thread, which will in turn create many other threads using Payneallel.  By default, Payneallel will block the calling thread until all operations are done, however we cannot both block the GUI thread AND allow it to update itself as test results are available.  The RunAsync method is simple:

        void RunAsync()

        {

            wrapper.BeginRunAssembly(TestCallback);

        }

 

ExecutorWrapper

My next modification is to the ExecutorWrapper class.  I tried to make my changes to XUnit additive only, adding functionality by adding methods rather than modifying things that already work for sequential execution.    

        public void BeginRunAssembly(Action<XmlNode> callback)

        {

            XmlNodeCallbackWrapper wrapper = new XmlNodeCallbackWrapper(callback);           

            CreateObject("XUnit.Sdk.Executor+RunAssemblyParallel", executor, wrapper);

        }

I see no reason not to keep running the test in a separate AppDomain.  We have added another inner class to Executor, the RunAssemblyParallel class. 

Executor

Through experimentation I found that this would be the appropriate place to introduce parallel execution, at the Class level as I said previously.  This class is almost a copy of the RunAssembly class included with XUnit:

        public class RunAssemblyParallel : MarshalByRefObject

        {

            /// <summary/>

            public RunAssemblyParallel(Executor executor, object _handler)

            {

                DoParallel(executor, _handler);

            }

 

            protected void DoParallel(Executor executor, object _handler)

            {

                ICallbackEventHandler handler = _handler as ICallbackEventHandler;

                AssemblyResult results = new AssemblyResult(new Uri(executor.assembly.CodeBase).LocalPath);

 

                Action<Type> doOne = delegate(Type type)

                {

                    ITestClassCommand testClassCommand = TestClassCommandFactory.Make(type);

 

                    if (testClassCommand != null)

                    {

                        ClassResult classResult = TestClassCommandRunner.Execute(testClassCommand,

                                                                                 null,

                                                                                 result => OnTestResult(result, handler));

                        results.Add(classResult);

                    }

                };

 

                Type[] exportedTypes = executor.assembly.GetExportedTypes();

                int count = exportedTypes.Length;

 

                //Parallel Test execution

                Stopwatch sw = new Stopwatch();

                sw.Start();

                Payneallel.ForEach<Type>(exportedTypes, doOne, true);

                sw.Stop();

                Console.WriteLine("Time elapsed: " + sw.Elapsed);

                results.ExecutionTime = sw.Elapsed.TotalSeconds;

                OnTestResult(results, handler);

            }

        }

 }

Like the TPL, Payneallel likes an Action<T> to execute.  In the vanilla XUnit version of this code, there is no StopWatch and there is a regular foreach() block instead of Payneallel.ForEach.  The stopwatch is important because I can no longer trust XUnit to time the execution!  For a long time I ran and re-ran my tests and the Parallel code was always slower than the sequential version.  Then I had a “pwop” moment and found the following line of code:

                ExecutionTime += child.ExecutionTime;

Whoops!  We can’t just add the execution time of the children (from TimedCommand) when some of the commands are running at the same time. 

Results

With the Timing issue solved, I was successfully executing unit tests concurrently and saving a lot of time doing so.  Here is the same set of unit tests ran using my new Concurrent xUnit hack.

I’ll take 27 seconds over 51 seconds any day, and I have not done any optimization work yet, nor constructed a test case where the tests are nearly 4x faster on a four processor machine, but I expect to be able to get there.  As I mentioned before, the Class is the unit of concurrency with this experiment, so the amount of time saved will depend heavily on how the test cases are structured.  A more ideal method would be to first get a list of all of the individual methods marked with [Fact] and use the parallel semantics on that list instead. 

I have a side project that is woefully under unit tested, code that I inherited.  I write unit tests for the code I touch as I refactor it.  The unit tests will involve a lot of database access, calculations, and Presenter mocking.  I can’t disclose what this codebase is just yet, but I am in the process of testing TestDrivenàXUnitàNCover.  I depend heavily on NCover and I really can’t imagine manually trying to determine what I’ve got test coverage on anymore.  If this test is successful, I will eventually be able to report on how this concurrent unit testing works on 100,000 lines of code 99% covered by thousands of unit tests.   This should be a sufficient test case to prove this idea is sound.

As the years go by and we still don’t have 5ghz machines, designing frameworks with concurrency in mind will become increasingly important.

Thursday, May 08, 2008 7:13:40 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Concurrent Unit Testing with xUnit – The answers come in dreams

Coil: the answers come in dreams

Well, not precisely in dreams, but in blog posts.  No sooner had I written (http://www.damonpayne.com/2008/04/17/ConcurrentUnitTesting.aspx) about how the over-design of NUnit was going to make it hard for me to implement concurrent unit testing than I see Scott Hanselman feature xUnit on his Daily Source Code (http://www.hanselman.com/blog/TheWeeklySourceCode24ExtensibilityEditionPlugInsProvidersAttributesAddInsAndModulesInNET.aspx ).  The words that caught my eye: the source is extremely tidy.  Scott probably meant that the organization of the solution was tidy but I grabbed it from CodePlex and started investigating.  The design is tidy too, could this be a better platform on which to complete my research?

I am generally liking xUnit.net so far, and I strongly expect I’ll be ditching NUnit in favor of this across the board assuming the integration with TestDriven and NCover work as I’d expect.  It’s nice to just say “using xUnit” instead of “using NUnit.Framework”, and I like that I don’t have to place a [TextFixture] attribute on the class.  But, these are small concerns saving a few keystrokes.  What we’re really concerned about is the original goal I wrote about:

On a sizable project, with a meaningful suite of unit tests, a developer practicing proper due diligence during the development lifecycle will spend a tremendous amount of time running Unit Tests.  This is an unfortunate disincentive for the developer to run said tests.

In general, the “Command” strucuture of a unit test foreshadows parallel-ability  Properly designed unit tests should be easy to run in parallel: a unit test should Stand Alone, meaning each test case does not depend on state set up elsewhere.  xUnit does two more things that help us out here.  The first is by removing the notions of “TestFixtureSetup/Teardown” they’ve made it much harder to shoot yourself in the foot at the Class level by relying on state, though for my example this is merely food for future thought as we’ll see later.  The second is that it would appear they use a Randomizer to make sure the [Fact] methods in a Class do not run in any dependable order. 

I set up a suite of 17 unit tests, implemented in 7 classes.  The tests do incredibly useful things like divide int.MaxValue by things and SpinWait().  Using xUnit is simple:

using XUnit;

 

namespace DamonPayne.xUnit.Tests

{

    public class FooTester : TestBase

    {

        [Fact]

        public void Fact1()

        {

            Console.WriteLine("FooTester::Fact1");

            System.Threading.Thread.SpinWait(int.MaxValue / 2);

            Assert.False(false);

        }

The last thing to do before jumping into code is to establish a baseline.   My 17 tests take 51.78 seconds to run in the xUnit GUI in all their spin-waiting glory.

Payneallel Revisited

While doing the research for this article, I found a few minor issues with my Payneallel.ForEach code I used with the Tree Concurrency articles (http://www.damonpayne.com/2008/04/03/ManagingConcurrencyWithTrees0.aspx ).  The first issue dealt with the code I used to wait for all concurrent iterations to be done before returning to the calling thread.  If the number of tasks was less than the number of processors on the machine, the “never touched” worker threads would never Finish().  The second dealt with an interesting thread-timing issue related to when a Worker declared itself “Busy”.  Concurrency is fun!  At any rate, here is the revised Payneallel code I used within xUnit:

using System;

using System.Collections.Generic;

using System.Threading;

 

namespace XUnit.Sdk

{

    /// <summary>

    /// Contains static methods and internal helper classes for executing concurrent operations

    /// </summary>

    public static class Payneallel

    {

        /// <summary>

        /// Concurrently perform the body action on each item in source

        /// </summary>

        /// <typeparam name="TSource"></typeparam>

        /// <param name="source"></param>

        /// <param name="body"></param>

        public static void ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body)

        {

            ForEach<TSource>(source, body, true);

        }

 

        /// <summary>

        ///

        /// </summary>

        /// <typeparam name="TSource"></typeparam>

        /// <param name="source"></param>

        /// <param name="body"></param>

        /// <param name="waitAll"></param>

        public static void ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body, bool waitAll)

        {

            WorkerPool<TSource> pool = new WorkerPool<TSource>();

 

            foreach (TSource src in source)

            {

                Worker<TSource> worker = pool.GetWorker();

                //Console.WriteLine("Using worker " + worker.Name);

                worker.Arg = src;

                worker.Work = body;

                worker.Go();

            }

 

            if (waitAll)

            {

                pool.WaitAll();

            }

        }

    }

   

 

    /// <summary>

    ///

    /// </summary>

    /// <typeparam name="T"></typeparam>

    class WorkerPool<T>

    {

 

        public WorkerPool()

        {

            _workers = new List<Worker<T>>(Environment.ProcessorCount);

 

            for (int i = 0; i < Environment.ProcessorCount; ++i)

            {

                Worker<T> worker = new Worker<T>("Payneallel " + i);

                _workers.Add(worker);

                worker.Done = new Action<T>(WorkerDone);

                worker.Go();

            }

 

            _workerDoneEvent = new ManualResetEvent(false);

        }

 

 

        private ManualResetEvent _workerDoneEvent;

        private static List<Worker<T>> _workers;

        private object _syncRoot = new object();

 

        /// <summary>

        ///

        /// </summary>

        public void WorkerDone<T>(T arg)

        {

            lock (_syncRoot)

            {

                _workerDoneEvent.Set();

            }

        }

 

 

 

        public Worker<T> GetWorker()

        {

            Worker<T> worker = GetFreeWorker();

 

            while (null == worker)

            {

                _workerDoneEvent.WaitOne(5, true);

                worker = GetFreeWorker();

            }

 

            _workerDoneEvent.Reset();

 

            return worker;

        }

 

 

 

        private Worker<T> GetFreeWorker()

        {

            foreach (Worker<T> w in _workers)

            {

                if (!w.Busy)

                {

                    //Console.WriteLine("returning worker from pool");

                    return w;

                }

            }

 

            return null;

        }

 

 

 

        public void WaitAll()

        {

            while (true)

            {

                foreach (Worker<T> w in _workers)

                {

                    string name = w.Name;

                    if (w.Busy)//Don't block if the thread is not working

                    {

                        w.Finish();

                    }

                    else

                    {

                        w.Active = false;

                    }

                }

 

                return;

            }

        }

 

    }

 

    /// <summary>

    ///

    /// </summary>

    /// <typeparam name="T"></typeparam>

    class Worker<T>