网络驱动程序中的性能

最小化发送和接收路径长度

尽管发送和接收路径因驱动程序而异,但性能优化有一些常规规则:

  • 针对通用路径进行优化。 Kernprof.exe工具随 Windows 的开发人员和 IDW 版本一起提供,用于提取所需信息。 开发人员应查看消耗最多 CPU 周期的例程,并尝试减少调用这些例程的频率或在这些例程中花费的时间。

  • 减少在 DPC 中花费的时间,以便网络适配器驱动程序不会使用过多的系统资源,这会导致整体系统性能受到影响。

  • 确保调试代码未编译为驱动程序的最终发布版本;这可避免执行过多代码。

对数据和代码进行分区以最大程度地减少处理器之间的共享

需要分区以最大程度地减少跨处理器的共享数据和代码。 分区有助于降低系统总线利用率并提高处理器缓存的有效性。 为了最大程度地减少共享,驱动程序编写者应考虑以下事项:

  • 将驱动程序实现为反序列化的微型端口,如 反序列化的 NDIS 微型端口驱动程序中所述。

  • 使用每个处理器的数据结构来减少全局和共享数据访问。 这样就可以在不同步的情况下保留统计信息计数器,从而缩短代码路径长度并提高性能。 对于重要统计信息,具有在查询时将加在一起的每个处理器计数器。 如果必须具有全局计数器,请使用互锁操作而不是旋转锁来操作计数器。 有关如何避免使用旋转锁的信息,请参阅下面的正确使用锁定机制。

    为了方便实现此目的, KeGetCurrentProcessorNumberEx 可用于确定当前处理器。 若要确定分配每个处理器数据结构时的处理器数,可以使用 KeQueryGroupAffinity

    关联掩码中设置的位总数指示系统中活动处理器的数目。 驱动程序不应假定掩码中的所有设置位都是连续的,因为处理器可能不会在操作系统的未来版本中连续编号。 SMP 计算机中的处理器数是从零开始的值。

    如果驱动程序维护每个处理器的数据,则可以使用 KeQueryGroupAffinity 函数来减少缓存行争用。

避免错误共享

当处理器请求相互独立的共享变量时,会发生错误共享。 但是,由于变量位于同一缓存行上,因此它们在处理器之间共享。 在这种情况下,缓存行将在处理器之间来回传输,以便每次访问其中的任何变量,从而导致缓存刷新和重新加载增加。 这会增加系统总线利用率并降低整体系统性能。

若要避免错误共享,请使用 NdisGetSharedDataAlignment 将重要数据结构 ((如旋转锁、缓冲区队列标头、单独链接列表) )对齐到缓存行边界。

正确使用锁定机制

如果未正确使用,旋转锁可能会降低性能。 驱动程序应尽可能使用互锁操作来最大程度地减少对旋转锁的使用。 但是,在某些情况下,旋转锁可能是出于某些目的的最佳选择。 例如,如果驱动程序在处理尚未指示回驱动程序的数据包数的引用计数时获取旋转锁,则无需使用互锁操作。 有关详细信息,请参阅 网络驱动程序中的同步和通知

下面是有关有效使用锁定机制的一些提示:

使用 64 位 DMA

64 位 DMA 如果网络适配器支持 64 位 DMA,则必须执行步骤以避免对超过 4 GB 范围的地址进行额外复制。 当驱动程序调用 NdisMRegisterScatterGatherDma 时,必须在 Flags 参数中设置NDIS_SG_DMA_64_BIT_ADDRESS 标志

确保缓冲区对齐方式正确

将数据从一个缓冲区复制到另一个缓冲区时,缓存行边界上的缓冲区对齐可以提高性能。 大多数网络适配器接收缓冲区在首次分配时会正确对齐,但最终必须复制到应用程序缓冲区的用户数据由于使用的标头空间而未对齐。 如果 TCP 数据 (最常见的方案) ,则 TCP、IP 和以太网标头导致的偏移会导致0x36字节的移动。 若要解决此问题,建议驱动程序分配稍大一点的缓冲区,并在0xA字节的偏移量插入数据包数据。 这将确保,在标头的 0x36 字节移动缓冲区后,用户数据正确对齐。 有关缓存线边界的详细信息,请参阅 NdisMAllocateSharedMemory 的“备注”部分。

使用 Scatter-Gather DMA

NDIS 散点/收集 DMA 为硬件提供将数据传入和传出不连续的物理内存范围的支持。 Scatter-Gather DMA 使用 SCATTER_GATHER_LIST 结构,其中包括 SCATTER_GATHER_ELEMENT 结构的数组和数组中的元素数。 此结构是从传递给驱动程序的 send 函数的数据包描述符中检索的。 数组的每个元素提供物理连续Scatter-Gather区域的长度和起始物理地址。 驱动程序使用长度和地址信息来传输数据。

使用 DMA 操作的Scatter-Gather例程可以通过不以静态方式锁定这些资源来提高系统资源的利用率,就像使用映射寄存器时那样。 有关详细信息,请参阅 NDIS 散点/收集 DMA

如果网络适配器支持 TCP 分段卸载 (大型发送卸载) ,则驱动程序需要将它可以从 TCP/IP 获取的最大缓冲区大小传递到 NdisMRegisterScatterGatherDma 函数中的 MaximumPhysicalMapping 参数。 这将保证驱动程序有足够的映射寄存器来生成Scatter-Gather列表,并消除任何可能的缓冲区分配和复制。 有关详细信息,请参阅以下主题:

支持接收方限制

为了最大限度地减少多媒体应用程序中媒体播放期间的中断,NDIS 6.20 及更高版本驱动程序在处理接收中断时必须支持接收端限制 (RST) 。 有关详细信息,请参阅:

NDIS 6.20 中的接收端限制微型端口驱动程序移植到 NDIS 6.20 所需的更改摘要中的“发送和接收代码路径”