I connect 15 asynchronous operations through ports and receivers. This bothered me a lot about the time of the internetwork messaging, in particular, the time taken to transfer the task data to the port, and the new task starts processing the same data in another thread. Assuming that at the beginning of each case, each thread is idle, I generated a test that uses the stopwatch class to measure time from two different dispatchers, each of which works with the highest priority on one thread.
What surprised me, my development setup was a Q6600 Quad Core 2.4 Ghz running Windows 7 x64, and the average context switching time from my test was 5.66 microseconds with a standard deviation of 5.738 microseconds and a maximum of almost 1.58 milliseconds (in 282 times!). The frequency of the stopwatch is 427.7 nanoseconds, so I still feel the sensor noise well.
What I would like to do is reduce the maximum possible messaging time and, just as importantly, reduce the standard deviation of the context switch. I understand that Windows is not an operational operating system, and there are no guarantees, but the window scheduler is a graph with fair circular priority distribution, and the two threads in this test both have the highest priority (the only threads that should be so high), therefore there should not be any contextual switches on the flows (it is obvious that the maximum time is 1.58 ms ... I believe that window quanta are 15.65 ms?) The only thing I can think of is changing the timing of OS calls to the locking mechanisms used by CCR for messaging between threads.
Please let me know if someone else has measured the messaging time, and there are suggestions for improving it.
Here is the source code from my tests:
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using Microsoft.Ccr.Core;
using System.Diagnostics;
namespace Test.CCR.TestConsole
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting Timer");
var sw = new Stopwatch();
sw.Start();
var dispatcher = new Dispatcher(1, ThreadPriority.Highest, true, "My Thread Pool");
var dispQueue = new DispatcherQueue("Disp Queue", dispatcher);
var sDispatcher = new Dispatcher(1, ThreadPriority.Highest, true, "Second Dispatcher");
var sDispQueue = new DispatcherQueue("Second Queue", sDispatcher);
var legAPort = new Port<EmptyValue>();
var legBPort = new Port<TimeSpan>();
var distances = new List<double>();
long totalTicks = 0;
while (sw.Elapsed.TotalMilliseconds < 5000) ;
int runCnt = 100000;
int offset = 1000;
Arbiter.Activate(dispQueue, Arbiter.Receive(true, legAPort, i =>
{
TimeSpan sTime = sw.Elapsed;
legBPort.Post(sTime);
}));
Arbiter.Activate(sDispQueue, Arbiter.Receive(true, legBPort, i =>
{
TimeSpan eTime = sw.Elapsed;
TimeSpan dt = eTime.Subtract(i);
distances.Add(dt.TotalMilliseconds);
if(distances.Count > offset)
Interlocked.Add(ref totalTicks,
dt.Ticks);
if(distances.Count < runCnt)
legAPort.Post(EmptyValue.SharedInstance);
}));
legAPort.Post(EmptyValue.SharedInstance);
Thread.Sleep(500);
while (distances.Count < runCnt)
Thread.Sleep(25);
TimeSpan exTime = TimeSpan.FromTicks(totalTicks);
double exMS = exTime.TotalMilliseconds / (runCnt - offset);
Console.WriteLine("Exchange Time: {0} Stopwatch Resolution: {1}", exMS, Stopwatch.Frequency);
using(var stw = new StreamWriter("test.csv"))
{
for(int ix=0; ix < distances.Count; ix++)
{
stw.WriteLine("{0},{1}", ix, distances[ix]);
}
stw.Flush();
}
Console.ReadKey();
}
}
}