Shawn Weisfeld

I find when I talk to myself nobody listens. - Shawn Weisfeld
posts - 365, comments - 174, trackbacks - 34

My Links

News


Shawn Weisfeld's Facebook profile

The views expressed in this blog are mine and mine alone, not that of my employer, Microsoft, or anyone else’s. No warrantee is given for the quality of any material on this site.

Archives

Post Categories

Using the CCR for Line of Business Applications

For fun I have been playing around with Microsoft Robotics studio (http://microsoft.com/robotics) for a long time now and I have listened to Microsoft’s George Chrysanthakopoulos tell us how cool the CCR was in just about every webcast. Well I have decided to see if I can use the CCR in a regular business application and eliminate some of the pains of writing multithreaded code.

To that end I started with a simple problem, calculating Fibonacci numbers, and like any good developer I started with an easy to implement sequential algorithm. Now that I could sequentially calculate the numbers I wanted to see if I can use the CCR to calculate the same results. This required creation of a new execute method, but note that I was able to reuse the code that I wrote to calculate 1 Fibonacci number, the only thing I am rewriting is the test harness. Finally I rewrote the code using a traditional threading model.

Calculating 10 Fibonacci numbers sequentially took about 13.7 seconds on my dual core laptop, but as one would expect only one of my cores was actually working the other one just sat there doing nothing. On the other hand when processing the same set of numbers with both the CCR or the traditional threading model it took about 7.3 seconds. This is due to the fact that now I was able to use both cores of my computer.

I know what you are saying, self, why use the CCR I could just use traditional threading techniques. IMHO the major benefit you get with the use of the CCR is that the delta between the sequential code you write and the CCR enabled code you write is much smaller.

The CCR makes heavy use of queuing logic. So when you have work to be performed you just shove it in a queue and attach a receive arbiter to tell the CCR what to do with each work item (message). The CCR takes care of all the plumbing required to schedule and coordinate each item.

When starting to work with the CCR I strongly recommend reading the following articles. All of witch I have shamelessly borrowed ideas from.

The Code

This is my entry point where I generate some random numbers and then pass them to each of the test harnesses to get processed.

    1             //Generate random number to calculate Fibonacci for

    2             int[] fibonacciCalculations = new int[10];

    3             Random r = new Random();

    4             for(int i = 0; i < fibonacciCalculations.Length; i++)

    5             {

    6                 fibonacciCalculations[i] = r.Next(30, 40);

    7             }

    8 

    9             SequentialExample.Execute(fibonacciCalculations);

   10             ThreadPoolExample.Execute(fibonacciCalculations);

   11             CcrExample.Execute(fibonacciCalculations);

   12 

   13             Console.WriteLine("Done!");

   14             Console.ReadKey();

Sequential Test Harness

    1     /// <summary>

    2     /// Method used to exercise the sequential algorithm

    3     /// </summary>

    4     public class SequentialExample

    5     {

    6         public static void Execute(int[] fibonacciCalculations)

    7         {

    8             DateTime start = DateTime.Now;

    9 

   10             //iterate over the collection, calculate and display the results

   11             for (int i = 0; i < fibonacciCalculations.Length; i++)

   12             {

   13                 SequentialFibonacci f = new SequentialFibonacci(fibonacciCalculations[i]);

   14                 f.Calculate();

   15                 Console.WriteLine("Answer: {0}", f.FibOfN);

   16             }

   17 

   18             //Display the execution time

   19             TimeSpan runtime = DateTime.Now.Subtract(start);

   20             Console.WriteLine("Sequential in {0} milliseconds.", runtime.TotalMilliseconds);

   21         }

   22     }

   23 

   24     /// <summary>

   25     /// Calculate a given Fibonacci number

   26     /// </summary>

   27     public class SequentialFibonacci

   28     {

   29         /// <summary>

   30         /// Default constructor

   31         /// </summary>

   32         /// <param name="n"></param>

   33         public SequentialFibonacci(int n)

   34         {

   35             _n = n;

   36         }

   37 

   38         /// <summary>

   39         /// Recursive method that calculates the Nth Fibonacci number.

   40         /// </summary>

   41         /// <param name="n"></param>

   42         /// <returns></returns>

   43         private int Calculate(int n)

   44         {

   45             if (n <= 1)

   46             {

   47                 return n;

   48             }

   49 

   50             return Calculate(n - 1) + Calculate(n - 2);

   51         }

   52 

   53         /// <summary>

   54         /// Calculate the fibonacci number for me

   55         /// </summary>

   56         public void Calculate()

   57         {

   58             _fibOfN = Calculate(_n);

   59         }

   60 

   61         public int N { get { return _n; } }

   62         private int _n;

   63 

   64         public int FibOfN { get { return _fibOfN; } }

   65         private int _fibOfN;

   66     }

CCR Test Harness

    1 public class CcrExample

    2     {

    3         public static void Execute(int[] fibonacciCalculations)

    4         {

    5             DateTime start = DateTime.Now;

    6 

    7             //Create the queues

    8             DispatcherQueue dq = new DispatcherQueue();

    9             Port<SequentialFibonacci> fibPort = new Port<SequentialFibonacci>();

   10             SuccessFailurePort results = new SuccessFailurePort();

   11 

   12             //Post the work to be completed

   13             for (int i = 0; i < fibonacciCalculations.Length; i++)

   14             {

   15                 fibPort.Post(new SequentialFibonacci(fibonacciCalculations[i]));

   16             }

   17 

   18             //Tell the CCR how to proccess each work item

   19             //(scatter the work over avaiable workers)

   20             Arbiter.Activate(dq,

   21                 Arbiter.Receive(true, fibPort,

   22                 delegate(SequentialFibonacci fib)

   23                 {

   24                     try

   25                     {

   26                         fib.Calculate();

   27                         Console.WriteLine("Answer: {0}", fib.FibOfN);

   28                         results.Post(new SuccessResult());

   29                     }

   30                     catch (Exception ex)

   31                     {

   32                         results.Post(ex);

   33                     }

   34                 }));

   35 

   36             //Tell the CCR to wait tell us when it is done

   37             //(Gather up all the results)

   38             Arbiter.Activate(dq,

   39                 Arbiter.MultipleItemReceive(results, fibonacciCalculations.Length,

   40                 delegate(ICollection<SuccessResult> successes, ICollection<Exception> failures)

   41                 {

   42                     TimeSpan runtime = DateTime.Now.Subtract(start);

   43                     Console.WriteLine("CCR in {0} milliseconds.", runtime.TotalMilliseconds);

   44                 }));

   45         }

   46 

   47     }

Thread Pool Test Harness

    1     /// <summary>

    2     /// Excerpt from How to: Use a Thread Pool (C# Programming Guide)

    3     /// http://msdn2.microsoft.com/en-us/library/3dasc8as(VS.80).aspx

    4     /// </summary>

    5     public class ThreadedFibonacci

    6     {

    7         public ThreadedFibonacci(int n, ManualResetEvent doneEvent)

    8         {

    9             _n = n;

   10             _doneEvent = doneEvent;

   11         }

   12 

   13         // Wrapper method for use with thread pool.

   14         public void ThreadPoolCallback(Object threadContext)

   15         {

   16             int threadIndex = (int)threadContext;

   17             _fibOfN = Calculate(_n);

   18             Console.WriteLine("Answer: {0}", _fibOfN);

   19             _doneEvent.Set();

   20         }

   21 

   22         // Recursive method that calculates the Nth Fibonacci number.

   23         public int Calculate(int n)

   24         {

   25             if (n <= 1)

   26             {

   27                 return n;

   28             }

   29 

   30             return Calculate(n - 1) + Calculate(n - 2);

   31         }

   32 

   33         public int N { get { return _n; } }

   34         private int _n;

   35 

   36         public int FibOfN { get { return _fibOfN; } }

   37         private int _fibOfN;

   38 

   39         private ManualResetEvent _doneEvent;

   40     }

   41 

   42     public class ThreadPoolExample

   43     {

   44         public static void Execute(int[] fibonacciCalculations)

   45         {

   46             DateTime start = DateTime.Now;

   47 

   48             // One event is used for each Fibonacci object

   49             ManualResetEvent[] doneEvents = new ManualResetEvent[fibonacciCalculations.Length];

   50             ThreadedFibonacci[] fibArray = new ThreadedFibonacci[fibonacciCalculations.Length];

   51 

   52             // Configure and launch threads using ThreadPool:

   53             for (int i = 0; i < fibonacciCalculations.Length; i++)

   54             {

   55                 doneEvents[i] = new ManualResetEvent(false);

   56                 ThreadedFibonacci f = new ThreadedFibonacci(fibonacciCalculations[i], doneEvents[i]);

   57                 fibArray[i] = f;

   58                 ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i);

   59             }

   60 

   61             // Wait for all threads in pool to calculation...

   62             WaitHandle.WaitAll(doneEvents);

   63 

   64             TimeSpan runtime = DateTime.Now.Subtract(start);

   65             Console.WriteLine("Threaded in {0} milliseconds.", runtime.TotalMilliseconds);

   66 

   67         }

   68     }

Print | posted on Tuesday, January 8, 2008 12:09 PM | Filed Under [ .NET C# Robotics ]

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 3 and 3 and type the answer here:

Powered by: