【问题标题】:Idendify the reason for a 200 ms freezing in a time critical loop确定时间关键循环中 200 毫秒冻结的原因
【发布时间】:2014-11-12 12:48:03
【问题描述】:

问题的新描述:

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

原问题:

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

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

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

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

【问题讨论】:

  • 如何切换缓冲区?另外,你说猜测是这个问题,你分析过吗?
  • 你为什么不使用一个巨大的环形缓冲区?为什么没有异步写入?
  • 你到底用双缓冲做什么?你有 [deque] (cplusplus.com/reference/deque/deque),一个线程最后推送记录(push_back),其他线程从前面读取(pop_front)。
  • @user3924882:虽然这不是线程安全的。如果你锁定 pop/push,那么重新分配呢?
  • 你的缓冲区是固定大小的吗?除非另一个核心处于深度节能状态,否则 200 毫秒不是核心关联问题。可能是分页,因为您确实可能在某些方法中错误地复制了缓冲区,这可能会导致此类事情。

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


【解决方案1】:

仅仅为了切换一个布尔变量而锁定和释放互斥体不会花费 200 毫秒。

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

为了进一步阅读,我推荐阅读this 文章,它更详细地描述了锁争用。

【讨论】:

  • 虽然这在理论上可以回答问题,it would be preferable 在这里包含答案的基本部分,并提供链接以供参考。
【解决方案2】:

由于您在 Windows 上运行,也许您使用 Visual Studio?如果是的话,一旦你不需要检查数据/指令缓存(那么英特尔的 vTune 是一个自然的选择),我会求助于在这种情况下非常好的 VS 分析器(恕我直言)。根据我的经验,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 获得
祝你好运

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-12
      • 2021-01-31
      • 2017-04-06
      相关资源
      最近更新 更多