我通过端口和接收器将 15 个异步操作链接在一起。这让我非常关心线程间消息传递时间,特别是从任务将数据发布到端口和新任务开始在不同线程上处理相同数据之间的时间。假设每个线程在启动时处于空闲状态的最佳情况,我生成了一个测试,该测试使用秒表类来测量来自两个不同调度程序的时间,每个调度程序都以最高优先级运行单个线程。
令我惊讶的是,我的开发设备是一台运行 Windows 7 x64 的 Q6600 四核 2.4 Ghz 计算机,我的测试的平均上下文切换时间为 5.66 微秒,标准偏差为 5.738 微秒,最大值接近 1.58 毫秒( 282 倍!)。秒表频率为 427.7 纳秒,所以我仍然远离传感器噪音。
我想做的是尽可能减少线程间消息传递时间,同样重要的是,减少上下文切换的标准偏差。我意识到 Windows 不是实时操作系统,也不能保证,但 Windows 调度程序是一个基于公平循环优先级的调度,并且此测试中的两个线程都处于最高优先级(唯一应该是的线程高),所以线程上不应该有任何上下文切换(从 1.58 毫秒的最大时间可以看出......我相信 windows quanta 是 15.65 毫秒?)我唯一能想到的是操作系统调用时间的变化CCR 用于在线程之间传递消息的锁定机制。
请让我知道是否有其他人测量过线程间消息传递时间,并对如何改进它有任何建议。
这是我测试的源代码:
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);
//if (distances.Count == 0 || Math.Abs(distances[distances.Count - 1] - dt.TotalMilliseconds) / distances[distances.Count - 1] > 0.1)
distances.Add(dt.TotalMilliseconds);
if(distances.Count > offset)
Interlocked.Add(ref totalTicks,
dt.Ticks);
if(distances.Count < runCnt)
legAPort.Post(EmptyValue.SharedInstance);
}));
//Thread.Sleep(100);
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();
}
}
}