网络驱动程序中的性能

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

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

  • 针对常见路径进行优化。 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 结构的数组和数组中的元素数。 从传递给驱动程序发送函数的数据包描述符中检索此结构。 数组的每个元素都提供物理连续 Scatter-Gather 区域的长度和起始物理地址。 驱动程序使用长度和地址信息来传输数据。

使用 Scatter-Gather 例程进行 DMA 操作能够提高系统资源的利用率,因为它不需要像使用映射寄存器那样对资源进行静态锁定。 有关详细信息,请参考 NDIS 散播/聚集 DMA

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

支持接收端调节

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

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