所以我一直在为我正在构建的服务器应用程序的套接字研究重叠IO,我不断看到人们说“永远不要使用hEvent
“或“IO完成端口会更快”等的评论,但从来没有人说为什么不使用hEvent
,也从来没有人提供任何关于完成端口更快的真实世界数据或数字,或者快多少。hEvent
与WaitForMultipleObjects()
更适合我的应用程序,所以如果速度差异很小,我倾向于使用它,但我不想在没有一些真实的数据告诉我我做出了多大的牺牲之前就承诺这一点。我一直在谷歌上搜索,搜索,搜索,但不能除了一些StackOverflow回答说“不要使用这个”而没有给出理由之外,找不到任何比较这两种策略的基准测试或文章或任何东西。
有人能提供一些真实的信息或数字,说明使用hEvent
和完成端口之间的实际差异吗?
3条答案
按热度按时间km0tfn4u1#
这个答案来自Harry约翰斯顿对这个问题的评论,经过一番搜索,我发现了一些使
WaitForMultipleObjects
成为一件可怕事情的更多细节。您可以等待的对象的最大数量是64。仅这一点就使得WFMO方法的可伸缩性几乎不存在。但进一步查看,我发现了以下线索:https://groups.google.com/forum/#!topic/comp.os.ms-windows.programmer.win32/okwnsYetF6g
在NT中,要进入等待状态,必须为每个对象分配一个等待块,每个等待块排队到您正在等待的对象,然后交叉链接到线程。当任何对象被通知时,所有这些等待块都必须出队,取消链接,所有这些都发生在DISPATCH_LEVEL,并且除了池分配和释放之外的所有操作都在调度器自旋锁保持的情况下发生。
(WFMO fAll == TRUE的开销更大。每次发出任何对象的信号时,都必须检查所有其他对象。您已经猜到了,这一切都发生在DISPATCH_LEVEL,调度器自旋锁保持。)
调度器级别的自旋锁可以防止线程在整个系统中的抢占和时间分片,即使是多核系统也是如此。这是一个可怕的问题,也是一个很好的理由,如果你正在等待超过3个对象,就永远不要使用
WFMO
(线程有3个预分配的等待块,如果你等待的是3个或更少,就可以避免很多这样的问题)。7ajki6be2#
为了获得最佳性能,你应该使用IO完成端口。套接字的数量没有限制。所有其他类似select的API只能服务1024个套接字,性能会迅速下降,沿着cpu使用率也会高于需要。
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365198(v=vs.85).aspx
你也可以看看这个关于异步i/o的很棒的演示,我认为这是任何打算编写中到大规模客户端服务器应用程序的人必须注意的。
历史时间:异步C++ -史蒂芬·辛普森[ACCU 2017] https://www.youtube.com/watch?v=Z8tbjyZFAVQ
在本演示文稿中,您将找到可用技术的完整描述和比较,沿着基准测试结果。非常值得花时间。
WaitForMultipleObjects()限制为64个句柄,这使得它无法处理任何涉及多个i/o流的操作。
oiopk7p53#
实际上,还有第三种方法来通知重叠I/O的完成,那就是使用一个重叠完成例程,该例程将排队到发起线程的APC队列中。
所有这些方法都足够有用。
基于事件的通知最大的缺点是它不能伸缩(你一次只能等待64个事件)。Win32事件并不是获得通知的最佳方式。这适用于多线程应用程序,这些应用程序执行很少的I/O,但不想等待初始线程完成,或者想执行一些有限的多路复用。
重叠完成例程的主要缺点是,您无法控制哪个线程获得通知,并且您需要将初始化线程置于可提醒的等待状态以运行完成例程。(使用MWMOEx等待消息,同时将主线程置于可报警等待状态),但不适用于可能从多线程中的任何线程发起I/O并执行大量I/O的现代高质量视频游戏或因特网服务。
IOCP适用于这样的情况:您有许多线程可能会启动I/O,执行大量I/O,并且/或者希望在某个任意线程(或者,本质上,任何可能的线程)上获得完成通知。相对于其他选项,它唯一不适合的应用程序类型是单线程UI应用程序。
我没有比较这三种方法的性能,我只是认为它们适用于不同的模式。我自己从来没有使用过事件通知,但在不同的项目中使用过完成例程和IOCP,尽管老实说,我已经有十年没有真正使用过完成例程了。