确定在时间关键循环中冻结200毫秒的原因

时间:2014-11-12 12:48:03

标签: c++ windows multithreading virtual-serial-port

问题的新描述:

我目前在测试环境中运行新的数据采集软件。该软件有两个主要线程。一个包含一个快速循环,它与硬件通信并将数据压入双缓冲区。每隔几秒钟,此循环冻结200毫秒。我做了几次测试,但没有一个让我弄清楚软件在等什么。由于软件相当复杂且测试环境也会干扰软件,我需要一种工具/技术来测试记录器线程在被阻塞200 ms时等待的内容。有什么工具可以实现这个目标?

原始问题:

在我们的数据采集软件中,我们有两个提供主要功能的线程。一个线程负责从不同的传感器收集数据,第二个线程负责将数据保存到大块的磁盘中。数据收集在双缓冲区中。它通常每个项目包含100000个字节,每秒最多可收集300个项目。一个缓冲区用于在数据收集线程中写入,一个缓冲区用于读取数据并将其保存到第二个线程中的磁盘。如果已读取所有数据,则切换缓冲区。缓冲区的切换似乎是一个主要的性能问题。每次缓冲区切换时,数据收集线程都会阻塞大约200毫秒,这太长了。然而,它偶尔发生,切换速度更快,几乎没有时间。 (测试PC:Windows 7 64位,i5-4570 CPU @ 3.2 GHz(4核),16 GB DDR3(800 MHz))。

我的猜测是,性能问题与核心之间交换的数据有关。只有当线程偶然在同一个核心上运行时,交换才会快得多。我想过以某种方式设置线程关联掩码以强制两个线程在同一个核心上运行,但这也意味着我失去了真正的并行性。另一个想法是让缓冲区在切换之前收集更多数据,但这会大大降低数据显示的更新频率,因为它必须等待缓冲区切换才能访问新数据。

我的问题是:是否有一种技术可以将数据从一个线程移动到另一个线程而不会干扰收集线程?

编辑:双缓冲区实现为两个std :: vectors,用作环形缓冲区。 bool(int)变量用于指示哪个缓冲区是活动写缓冲区。每次访问双缓冲区时,都会检查bool值以了解应该使用哪个向量。切换双缓冲区中的缓冲区只意味着切换此bool值。当然,在切换期间,所有读取和写入都被互斥锁阻止。我不认为这个互斥锁可能会阻塞200毫秒。顺便说一句,200 ms对于每个开关事件都是非常可重复的。

3 个答案:

答案 0 :(得分:2)

锁定和释放互斥锁只是为了切换一个bool变量不会花费200ms。

主要问题可能是两个线程以某种方式相互阻塞。 这种阻塞称为lock contention。基本上,每当一个进程或线程尝试获取另一个进程或线程持有的锁时,就会发生这种情况。而是并行性,你有两个线程等待彼此完成他们的工作,具有与单线程方法类似的效果。

为了进一步阅读,我建议阅读this一篇文章,其中介绍了具有更详细级别的锁争用。

答案 1 :(得分:1)

既然您在Windows上运行,也许您使用visual studio?如果是的话,我会在这种情况下使用VS profiler,这是非常好的(恕我直言),一旦你不需要检查数据/指令缓存(那么英特尔的vTune是一个自然的选择)。根据我的经验,VS足以捕获争用问题以及CPU瓶颈。您可以直接从VS或作为独立工具运行它。如果您不需要在测试机器上安装VS,则可以复制该工具并在本地运行。

VSPerfCmd.exe / start:SAMPLE / attach:12345 / output:samples - 附加到进程12345并收集CPU采样信息
VSPerfCmd.exe / detach:12345 - 与进程分离
VSPerfCmd.exe / shutdown - 关闭探查器,编写samples.vsp(见第一行)

然后你可以打开文件并在visual studio中检查它。如果你没有看到任何让你的CPU忙于切换到争用分析的东西 - 只需改变" start"来自" SAMPLE"到" CONCURRENCY"

该工具位于%YourVSInstallDir%\ Team Tools \ Performance Tools \下,AFAIR可从VS2010获得 祝你好运

答案 2 :(得分:0)

在讨论了聊天中的问题后,结果发现Windows性能分析器是一个合适的工具。该软件是Windows SDK的一部分,可以在命令窗口中使用命令wprui打开。 (Alois Kraus在聊天中发布了这个有用的链接:http://geekswithblogs.net/akraus1/archive/2014/04/30/156156.aspx)。以下步骤揭示了该软件一直在等待的内容:

  • 使用默认设置使用WPR记录信息,并将保存的文件加载到WPA中。
  • 确定相关主题。在这种情况下,记录线程和保存线程显然具有最高的CPU负载。可以轻松识别保存线程。由于它将数据保存到光盘,因此它具有文件访问权限。 (查看内存 - >硬故障)
  • 查看计算 - > CPU使用率(精确)并选择按进程利用率,线程。选择您要分析的流程。最好按顺序显示列:NewProcess,ReadyingProcess,ReadyingThreadId,NewThreadID,[yellow bar],Ready(μs)sum,Wait(μs)sum,Count ...
  • 在ReadyingProcess下,我查找了具有最大等待时间(μs)的进程,因为我预计这个进程将对延迟负责。
  • 在ReadyingThreadID下,我检查了引用具有NewThreadId列中的延迟的线程的每一行。经过短暂的搜索,我发现一个线程显示频繁的等待时间约为100毫秒,总是显示为一对。在ReadyingThreadID列中,我能够读取记录循环正在等待的线程的id。
  • 根据它的CPU使用情况,这个线程基本没做什么。在我们的特殊情况下,这导致我假设串口io命令可能导致这种等待。停用后,延迟消失了。重要的发现是200毫秒延迟实际上由两个100毫秒的延迟组成。

进一步的分析表明,通过虚拟串口对获取数据命令有时会丢失。这可能与数据保存和压缩循环中的非常高的CPU负载有关。如果fetch命令丢失,则不会收到任何数据,第一次和第二次尝试接收数据的时间超过100 ms。