Производительность в сетевых драйверах

Минимизация длины пути отправки и получения

Хотя пути отправки и получения отличаются от драйвера к драйверу, существуют некоторые общие правила оптимизации производительности:

  • Оптимизируйте наиболее вероятные маршруты. Средство Kernprof.exe включено в сборки для разработчиков и сборки IDW Windows, которое извлекает необходимые сведения. Разработчик должен посмотреть на подпрограммы, которые используют большинство циклов ЦП и попытаться уменьшить частоту вызова этих подпрограмм или время, затраченное на эти подпрограммы.

  • Сократите время, затраченное на DPC, чтобы драйвер сетевого адаптера не использовал чрезмерные системные ресурсы, что приведет к снижению производительности системы.

  • Убедитесь, что код отладки не компилируется в окончательную выпущенную версию драйвера; Это позволяет избежать выполнения избыточного кода.

Секционирование данных и кода для минимизации общего доступа между процессорами

Секционирование необходимо для минимизации общих данных и кода между процессорами. Секционирование помогает сократить использование системной шины и повысить эффективность кэша процессора. Чтобы свести к минимуму совместное использование, разработчики драйверов должны учитывать следующее:

  • Реализуйте драйвер как десериализированный минипорт, как описано в десериализированных драйверах минипорта NDIS.

  • Используйте структуры данных для каждого процессора, чтобы уменьшить глобальный и общий доступ к данным. Это позволяет поддерживать счетчики статистики без синхронизации, что снижает длину пути кода и повышает производительность. Для жизненно важной статистики есть счетчики для каждого процессора, которые добавляются вместе во время запроса. Если вам необходимо иметь глобальный счетчик, используйте атомарные операции вместо спин-блокировок для управления счетчиком. См. раздел ниже "Правильное использование механизмов блокировки", чтобы избежать использования спинлоков.

    Для упрощения этого можно использовать KeGetCurrentProcessorNumberEx для определения текущего процессора. Чтобы определить количество процессоров при выделении структур данных для каждого процессора, можно использовать KeQueryGroupAffinity .

    Общее количество битов, заданных в маске сходства, указывает количество активных процессоров в системе. Драйверы не должны предполагать, что все наборы битов в маске будут последовательными, так как процессоры могут не быть последовательно нумерованы в будущих выпусках операционной системы. Число процессоров на компьютере SMP — это отсчитываемое от нуля значение.

    Если драйвер поддерживает данные на процессор, можно использовать функцию KeQueryGroupAffinity для уменьшения состязаний в строке кэша.

Избегайте ложного общего доступа

Ложный общий доступ возникает, когда процессоры запрашивают общие переменные, не зависящие друг от друга. Однако, поскольку переменные находятся в одной строке кэша, они совместно используются между процессорами. В таких ситуациях строка кэша будет перемещаться между процессорами для каждого доступа к любым переменным в нем, что приводит к увеличению сброса кэша и перезагрузки. Это повышает загрузку системной шины и снижает общую производительность системы.

Чтобы избежать ложного разделённого доступа, выравнивайте важные структуры данных (такие как блокировки спина, заголовки буферной очереди, последовательно связанные списки) по границам строки кэша с помощью NdisGetSharedDataAlignment.

Правильное использование механизмов блокировки

Спинлоки могут снизить производительность, если их используют неправильно. Водители должны свести к минимуму использование спин-блокировок, применяя атомарные операции там, где это возможно. Однако в некоторых случаях блокировка спина может быть лучшим выбором для некоторых целей. Например, если драйвер получает спиновую блокировку при управлении счётчиком количества пакетов, которые не были указаны обратно в драйвер, не требуется использовать атомарную операцию. Дополнительные сведения см. в Синхронизация и Уведомление в сетевых драйверах.

Ниже приведены некоторые советы по эффективному использованию механизмов блокировки:

  • Используйте функции однонаправленного списка NDIS, такие как следующие, для управления пулами ресурсов.

    NdisInitializeSListHead

    NdisInterlockedPushEntrySList

    NdisInterlockedPopEntrySList

    NdisQueryDepthSList

  • Если вам нужно использовать спинлоки, используйте их только для защиты данных, а не кода. Не используйте одну блокировку для защиты всех данных, используемых в обычных путях доступа. Например, разделите данные, используемые в пути отправки и получения, на две структуры данных, чтобы, когда путь отправки должен заблокировать свои данные, путь получения не затрагивался.

  • Если вы используете спинлоки и путь уже находится на уровне DPC, используйте функции NdisDprAcquireSpinLock и NdisDprReleaseSpinLock, чтобы избежать лишнего кода при получении и освобождении блокировок.

  • Чтобы свести к минимуму количество захвата и освобождения спин-блокировки, используйте следующие функции NDIS RWLock:

    NdisAllocateRWLock

    NdisAcquireRWLockRead

    NdisAcquireRWLockWrite

    NdisReleaseRWLock

Использование 64-разрядной DMA

64-разрядная DMA, если сетевой адаптер поддерживает 64-разрядную DMA, необходимо выполнить шаги, чтобы избежать дополнительных копий адресов выше диапазона 4 ГБ. Когда драйвер вызывает NdisMRegisterScatterGatherDma, в параметре Flags необходимо задать флаг NDIS_SG_DMA_64_BIT_ADDRESS.

Обеспечение правильного выравнивания буфера

Выравнивание буфера на границе строки кэша повышает производительность при копировании данных из одного буфера в другой. Большинство буферов сетевых адаптеров для приема правильно выравниваются при первом выделении, но пользовательские данные, которые в конечном итоге должны быть скопированы в буфер приложения, невыравнены из-за занимаемого пространства заголовка. В случае данных TCP (наиболее распространенный сценарий) сдвиг, связанный с заголовками TCP, IP и Ethernet, приводит к смещению на 0x36 байтов. Чтобы устранить эту проблему, мы рекомендуем драйверам выделить чуть больший буфер и вставить данные пакетов со смещением на 0xA байт. Это обеспечит, что после сдвига буферов на 0x36 байт для заголовка пользовательские данные будут правильно выровнены. Дополнительные сведения о границах строки кэша см. в разделе "Примечания" для NdisMAllocateSharedMemory.

Использование Scatter-Gather DMA

NDIS Scatter-Gather DMA предоставляет оборудование с поддержкой передачи данных в неконтинуальные диапазоны физической памяти. Scatter-Gather DMA использует структуру SCATTER_GATHER_LIST, которая включает массив структур SCATTER_GATHER_ELEMENT и количество элементов в массиве. Эта структура извлекается из дескриптора пакета, переданного функции отправки драйвера. Каждый элемент массива предоставляет длину и начальный физический адрес непрерывной области Scatter-Gather. Драйвер использует сведения о длине и адресе для передачи данных.

Использование рутин Scatter-Gather для операций DMA может улучшить использование системных ресурсов, не блокируя эти ресурсы статически, как это происходит при использовании регистров карты. Дополнительные сведения см. в разделе NDIS Scatter/Gather DMA.

Если сетевой адаптер поддерживает разгрузку сегментации TCP (разгрузка больших пакетов), драйверу потребуется передать максимальный размер буфера, который он может получить из TCP/IP, в параметр MaximumPhysicalMapping в функции NdisMRegisterScatterGatherDma. Это гарантирует, что у драйвера будет достаточно регистров карты для построения списка Scatter-Gather и предотвращения возможных выделений буферов и копирования. Дополнительные сведения см. в следующих разделах:

Поддержка регулирования на стороне приема

Чтобы свести к минимуму перебои во время воспроизведения мультимедиа в мультимедийных приложениях, драйверы NDIS 6.20 и более поздних версий должны поддерживать ограничение на стороне получения (RST) в обработке прерываний приема. Дополнительные сведения можно найти здесь

Регулятор на стороне приема в NDIS 6.20 "Маршруты отправки и приема кода" в Сводке изменений, необходимых для переноса минипорт-драйвера на NDIS 6.20