【问题标题】:Efficient Overlapped I/O for a socket server套接字服务器的高效重叠 I/O
【发布时间】:2011-02-12 15:36:41
【问题描述】:

这两种不同的模型中哪一种会更有效(考虑抖动、处理器缓存的利用、整体设计、一切等等)?

  1. 1 IOCP 和启动 X 线程(其中 X 是计算机拥有的处理器数量)。这意味着我的“服务器”对于所有请求和 X 线程只有 1 个 IOCP(队列)来服务/处理它们。我已经阅读了许多讨论这种设计效率的文章。使用此模型,我将拥有 1 个也与 IOCP 关联的侦听器。让我们假设我可以弄清楚如何保持数据包/请求同步。

  2. X IOCP(其中 X 是计算机拥有的处理器数量)并且每个 IOCP 有 1 个线程。这意味着每个处理器都有自己的队列和 1 个线程来服务/处理它们。使用此模型,我将有一个单独的侦听器(不使用 IOCP)来处理传入连接并将 SOCKET 分配给正确的 IOCP(创建的 X 之一)。让我们假设我可以弄清楚负载平衡。

对两种设计(一家银行)使用过于简化的类比:

  1. 一行有几个收银员来处理交易。每个人都排在同一条线上,每个收银员接下一个可用的人。

  2. 每个收银员都有自己的线路,人们被“放置”到其中一条线路中

在这两种设计之间,哪一种更高效。在每个模型中,重叠 I/O 结构将使用带有 MEM_COMMIT 的 VirtualAlloc(而不是“新”),因此交换文件不应成为问题(无分页)。根据向我描述的方式,将 VirtualAlloc 与 MEM_COMMIT 一起使用,内存被保留并且不会被分页。这将允许 SOCKETS 将传入数据直接写入我的缓冲区,而无需通过中间层。所以我不认为颠簸应该是一个因素,但我可能错了。

有人告诉我#2 会更有效率,但我没有听说过这个模型。提前感谢您的 cmets!

【问题讨论】:

  • 我不确定,但 #2 是否有效?是否可以通过多个不同的线程在同一个套接字上监听?
  • 是的,有可能。至少,在 unix 上,我确信 windows 在这方面没有什么不同
  • 我要做的是有一个单独的 Listener 对象来监听传入的连接。该对象会将 SOCKET 分配给 IOCP 之一。每个 IOCP 都不会听。

标签: windows sockets visual-c++ performance io-completion-ports


【解决方案1】:

我假设对于#2,您计划手动将您的套接字与您在接受套接字时基于某种“好”度量标准认为是“最佳”的 IOCP 相关联?不知何故,这种“善良”的衡量标准会在插座的整个生命周期内持续存在?

使用 IOCP 使用“标准”方式,即您的选项编号 1,内核会计算出如何最好地使用您拥有的线程,并允许更多线程在其中任何一个阻塞时运行。使用您的方法,假设您以某种方式解决了如何分配工作,您最终将运行比选项 1 更多的线程。

您的 #2 选项还阻止您使用 AcceptEx() 进行重叠接受,这比使用普通接受循环更有效,因为您从场景中删除了一个线程(以及由此产生的上下文切换和潜在的争用)。

你的比喻不成立;实际上,更多的情况是,要么有 1 个队列,有 X 个银行柜员,你加入队列并知道你会以有效的顺序被看到,而不是每个柜员都有自己的队列,你不得不猜测你加入的队列不包含一大堆想要开设新账户的人,而您旁边的一个包含一大堆只想做一些支付的人。单一队列确保您得到有效处理。

我认为您对MEM_COMMIT 感到困惑。这并不意味着内存不在分页文件中并且不会被分页。将VirtualAlloc 用于重叠缓冲区的通常原因是确保页面边界对齐,从而减少为 I/O 锁定的页面数量(可以在页面边界上分配页面大小的缓冲区,因此只占用一页而不是由于内存管理器决定使用不在页面边界开始的块而发生跨越两个)。

总的来说,我认为您正在尝试提前优化某些内容。首先以正常方式使用 IOCP 获得高效的服务器,然后对其进行分析。我严重怀疑你甚至需要担心构建你的#2 版本......同样,使用new 分配你的缓冲区开始,然后当你发现你的服务器时切换到VirtualAlloc() 的附加复杂性由于ENOBUFS 而失败,您确定这是由 I/O 锁定页面限制引起的,而不是缺少非分页池(您确实意识到您必须为VirtualAlloc() 分配“分配粒度”大小的块? )。

无论如何,我有一个免费的 IOCP 服务器框架,可以在这里使用:http://www.serverframework.com/products---the-free-framework.html,它可能会帮助您入门。

已编辑:您建议的复杂版本在某些 NUMA 架构中可能很有用,在这些架构中您使用 NIC 组合让交换机将您的流量分配到多个 NIC,将每个 NIC 绑定到不同的物理处理器和然后将您的 IOCP 线程绑定到同一个处理器。然后,您从该 NUMA 节点分配内存,并有效地让您的网络交换机对您的 NUMA 节点之间的连接进行负载平衡。恕我直言,我仍然建议最好获得一个工作服务器,您可以使用首先使用 IOCP 的“正常”方法进行分析,并且只有在您知道跨 NUMA 节点问题实际上会影响您的性能转向更复杂的情况时建筑...

【讨论】:

  • Hey Len,是的,我已经看到了您的免费代码(实际上仍在审查它)。您有免费版和许可版吗?感谢您对 MEM_COMMIT 的澄清。这就是宣传#2 的人向我描述的方式。我也知道 VirtualAlloc 的页面边界分配。另外,是的,我会使用一个线程来等待新的连接(使用#2),并将 SOCKET 与 IOCP 相关联,这将在连接期间保持不变。我实际上会使用 AcceptEx 并设置 OVERLAPPED 结构的 hEvent 。 (如果我没记错的话)
  • 我相信您在某处提到过,当在同一台服务器上运行多个(即一个用于端口 443,另一个用于端口 993 等)时,您的免费版本无法正常工作。这是我们可能会发生的情况。
  • 您不需要将OVERLAPPED 中的hEvent 与IOCP 一起使用。 #2错了;就这么简单,它的性能不如#1。免费代码对多台服务器的资源效率较低,但它们仍然可以正常工作。
  • 是的,我的意思是如果侦听器与 IOCP 分开(需要 #2),我将使用 hEvent。如果与 IOCP 一起使用,则不需要 hEvent。对困惑感到抱歉。感谢您确认 #2 效率较低。我假设它是但想要一个健全性检查。与我交谈的那个人不是我在工作中轻易不同意的人。 ;) 我将继续查看您的免费解决方案。再次感谢您的所有帮助!
【解决方案2】:

队列理论告诉我们,单个队列比多个队列具有更好的特性。你可以通过偷工减料来解决这个问题。

多队列方法应该有更好的缓存行为。是否明显更好取决于有多少接收到的数据包与单个事务相关联。如果一个请求适合单个传入的数据包,那么即使使用单个 IOCP 方法,它也会关联到单个线程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-23
    • 2011-12-04
    • 2012-02-22
    • 2012-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多