我通过端口和接收器链15个的异步操作在一起。这给我留下非常关心线程间通讯的时间,特别是需要对端口的任务发布数据之间的时间,和新的任务开始处理在不同的线程相同的数据。假定最好的情况情况,其中每个线程是在启动空闲,我已经产生其使用秒表类测量从两个不同的调度的时间在最高优先级的每个操作与单一线程。测试

我找到了什么让我感到惊讶,我的发展钻机是Q6600四核2.4GHz的计算机运行的是Windows 7 64位系统,并从我的试验中的平均上下文切换时间为5.66微秒的5.738微秒的标准偏差,最大的近1.58毫秒(282的一个因素!)。秒表频率是427.7纳秒,所以我仍然很好出传感器的噪声。

我想要做的就是减少线程间通讯的时间尽可能多地,同样重要的是,减少上下文切换的标准偏差。我知道Windows不是一个实时操作系统,有没有担保,但Windows调度是一个公平循环赛基于优先级调度,并在本次测试的两个线程都处于最高优先级(唯一的线程应该是高),所以不应该有上线(由1.58毫秒显而易见的任何上下文切换时间最多......我相信窗口量子是15.65毫秒?)我能想到的唯一的事情是在OS调用的时序变化由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();
        }
    }
}
有帮助吗?

解决方案

Windows不是一个实时操作系统。但是你已经知道了。什么是杀了你是上下文切换时间,不一定消息倍。你真的不指定如何您的流程间通信工作。如果你真的只是运行多个线程,你会通过不使用Windows消息作为通信协议,找到了一些收益,而不是尝试使用应用程序托管消息队列,而不是滚动您自己IPC。

您可以期待的最好结果平均为1ms与任何版本的Windows时,上下文切换发生。你可能看到1ms的时候,您的应用程序产生的内核。这是由设计为环1的应用程序(用户空间)。如果这是绝对关键的是你得到以下1ms的你需要一些应用程序切换到环0,这意味着编写设备驱动程序。

设备驱动程序不遭受同样的上下文切换时间该用户应用程序做,并有机会获得纳米第二分辨率定时器和睡眠呼叫以及。如果你需要做到这一点,DDK(设备驱动程序开发工具包)是免费提供从Microsoft,但我会强烈建议您在第三方开发工具的投资。他们通常有很好的样本和大量的向导来设置的东西,会带你阅读DDK文件,发现右几个月。您还想要得到的东西像SOFTICE因为正常的Visual Studio调试器是不会帮你调试的设备驱动程序。

其他提示

请15个异步操作有无是异步的?即你不得不通过一些图书馆的限制,以这种方式操作,或者你可以选择进行同步调用?

如果你有选择,你需要使异步的使用是由配置参数控制,以构建应用程序。即在不同的线程与同步操作返回异步操作之间的差值相同的线程上返回应该在代码透明。这样你可以调整它在不改变码结构。

短语“尴尬的并行”描述了其中大部分工作正在做的算法是独立于明显,因此可以以任何顺序来完成,因此很容易parallelise。

但你是“通过端口和接收机链15个异步操作起来”。这可以被描述为“令人尴尬的顺序”。换句话说,同样的程序可以被逻辑地写在一个单独的线程。但后来你会失去任何并行的异步操作之间的CPU限制的工作发生的历史(假设有任何意义的)。

如果你写一个简单的测试,切出任何显著CPU限制的工作,只是衡量上下文切换时间,那么你猜怎么着,你要被测量的上下文切换时间的变化,因为你已经发现

运行多线程的唯一原因是因为你有显著大量工作,为CPU的做,等你想分享出来几个CPU之间。如果计算的各个块是短命的不够,那么上下文切换将是对的任何的OS一个显著的开销。通过打破你的计算分成15个阶段,每一个很短,你基本上是问OS做了很多不必要的上下文切换。

ThreadPriority.Highest并不意味着只线程调度器本身具有更高的优先级。在Win32 API具有线程优先级的更精细的级别(如可以在任何硬件驱动程序/内核模式代码clicky )与几个级别以上最高(IIRC最高的是典型的优先级最高的非管理员代码可以在运行时,管理员可以安排较高的优先级),所以也不能保证他们会不被抢先。

即使线程优先级最高的窗户可以运行,如果他们持有较高优先级的线程需要这是另一种可能性,为什么你会痛苦的上下文切换资源锁推广自己的基本优先级以上的其他线程。

即使是这样,就像你说的,Windows不是一个实时操作系统,它不能保证反正兑现线程优先级。

要攻击这个问题的不同方式,你需要有这么多的去耦异步操作?这可能是考虑有用:垂直划分的工作(异步处理数据端到端的numCores组块),而不是水平地划分工作(如现在,在15个解耦阶段处理的数据的每个块);同步地耦合一些您的15个阶段的总减少到一个较小的数量。

线程间通信的开销始终是非平凡的。如果某些15个操作都做只是工作的一小块,上下文切换会咬你。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top