2

我环顾四周,似乎找不到我要找的东西,但首先让我强调一下,我不是在寻找高精度的睡眠功能。

这是我要解决的问题的背景:

我制作了一个内存映射库,它的操作很像命名管道。您可以将字节放入其中,从中获取字节,并查询有多少字节可用于读/写,所有这些都是好东西。

如果它们传递 8KB 或更大的字节块,使用它进行通信的快速(主要)进程平均速度为 4GB/s。当您接近 512B 块大小时,性能下降到大约 300MB/s。

问题:

偶尔,在负载较重的服务器上,会出现非常大的延迟时间(超过 5 秒)。我对这个问题的原因的运行理论是,当发生大量传输(大于映射内存的大小)时,写入数据的进程将紧密轮询以等待已实现的循环缓冲区中有更多可用空间在内存映射的顶部。没有睡眠调用,因此轮询过程可能会无缘无故地占用 CPU!问题是,即使是最小的睡眠调用(1ms)也绝对会破坏性能。memmap 大小为 16KB,因此如果它每 16KB 休眠 1ms,性能将下降到 16MB/s 的最佳情况。

解决方案:

我想要一个我可以调用的函数,它将放弃 CPU,但对操作系统重新调度它的时间没有限制(在本例中为 Windows 7)。

有没有人有任何好的选择?/有没有人知道这样的功能是否存在?

谢谢。

4

5 回答 5

3

根据 MSDN 文档,在 XP 或更高版本上,当您Sleep以 0 超时调用时,将让给具有相同优先级的其他进程。

零值会导致线程将其时间片的剩余部分让给任何其他准备运行的具有相同优先级的线程。如果没有其他同等优先级的线程准备运行,则函数立即返回,线程继续执行。

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686298(v=vs.85).aspx

另一个需要更多工作但工作更可靠的选择是在生产者和消费者进程之间共享事件句柄。您可以使用它CreateEvent来创建您的事件并将DuplicateHandle其纳入您的其他流程。当生产者填充缓冲区时,它将调用ResetEvent事件句柄并WaitForSingleObject使用它调用。当消费者从完整的共享缓冲区中删除了一些数据时,它会调用SetEvent,这将唤醒正在等待的生产者WaitForSingleObject

于 2013-08-21T01:14:32.313 回答
1

你需要这个SwitchToThread()函数(它只会在其他东西可以运行时放弃它的时间片),而不是 Sleep(0)(即使没有其他东西可以运行它也会放弃它的时间片)。

如果您正在编写旨在利用超线程的代码,YieldProcessor也可能会为您做一些事情,但我怀疑这会有所帮助。

于 2013-08-21T01:24:24.160 回答
1

您错误地假设了二元选择。你现在总是忙着等待,因为睡觉总是一个坏主意。

更好的解决方案是不睡觉尝试几次。如果仍然失败(因为映射已满,并且另一个线程没有运行),那么您可以发出真正的睡眠。这将是非常罕见的,平均而言你会睡几微秒。您甚至可以检查实时时钟 ( RDTSC) 以确定在放弃时间片之前您忙于等待的时间。

于 2013-08-21T09:28:14.320 回答
1

std::this_thread::yield()可能做你想要的。我相信它只是在大多数实现中调用Sleep with 0 。

于 2013-08-21T01:15:56.663 回答
0

如果您在.Net 下运行,您可以查看该Thread::Yield()方法。

它可能会或可能不会对您的特定场景有所帮助,但这是通知调度程序您要放弃剩余时间片的正确方法。

如果您在预 .Net 环境中运行(如果您在 Windows 7 上似乎不太可能),您可以查看 Win32SwitchToThread()函数。

于 2013-08-21T01:11:43.537 回答