Leistung in Netzwerktreibern
- Minimieren der Länge des Sende- und Empfangspfads
- Partitionieren von Daten und Code zur Minimierung der prozessorübergreifenden Freigabe
- Vermeiden von falscher Freigabe
- Ordnungsgemäßes Verwenden von Sperrmechanismen
- Verwenden von 64-Bit-DMA
- Sicherstellen einer ordnungsgemäßen Pufferausrichtung
- Verwenden von Scatter-Gather DMA
- Unterstützung der empfangsseitigen Drosselung
Minimieren der Länge des Sende- und Empfangspfads
Obwohl sich die Sende- und Empfangspfade von Treiber zu Treiber unterscheiden, gibt es einige allgemeine Regeln für Leistungsoptimierungen:
Optimieren Sie für die allgemeinen Pfade. Das Kernprof.exe-Tool wird mit den Entwickler- und IDW-Builds von Windows bereitgestellt, die die erforderlichen Informationen extrahieren. Der Entwickler sollte sich die Routinen ansehen, die die meisten CPU-Zyklen verbrauchen, und versuchen, die Häufigkeit dieser Routinen, die aufgerufen werden, oder die in diesen Routinen aufgewendete Zeit zu reduzieren.
Verringern Sie die in DPC aufgewendete Zeit, damit der Netzwerkadaptertreiber keine übermäßigen Systemressourcen verbraucht, was zu einer Einbuße der Gesamtsystemleistung führen würde.
Stellen Sie sicher, dass Debugcode nicht in die endgültige veröffentlichte Version des Treibers kompiliert wird. Dadurch wird die Ausführung von überschüssigem Code vermieden.
Partitionieren von Daten und Code zur Minimierung der prozessorübergreifenden Freigabe
Partitionierung ist erforderlich, um freigegebene Daten und Code prozessorenübergreifend zu minimieren. Partitionierung trägt dazu bei, die Systembusauslastung zu reduzieren und die Effektivität des Prozessorcaches zu verbessern. Um die Freigabe zu minimieren, sollten Treiberautoren Folgendes berücksichtigen:
Implementieren Sie den Treiber als deserialisierten Miniport, wie unter Deserialisierte NDIS-Miniporttreiber beschrieben.
Verwenden Sie Datenstrukturen pro Prozessor, um den globalen und freigegebenen Datenzugriff zu reduzieren. Dadurch können Sie Statistikindikatoren ohne Synchronisierung beibehalten, wodurch die Länge des Codepfads reduziert und die Leistung erhöht wird. Für wichtige Statistiken verfügen Sie über Indikatoren pro Prozessor, die zur Abfragezeit addiert werden. Wenn Sie einen globalen Leistungsindikator benötigen, verwenden Sie verriegelte Vorgänge anstelle von Drehsperren, um den Zähler zu bearbeiten. Informationen zum Vermeiden von Spinsperren finden Sie weiter unten unter Verwenden von Sperrmechanismen ordnungsgemäß.
Um dies zu erleichtern, kann KeGetCurrentProcessorNumberEx verwendet werden, um den aktuellen Prozessor zu bestimmen. Um die Anzahl der Prozessoren bei der Zuweisung von Datenstrukturen pro Prozessor zu bestimmen, kann KeQueryGroupAffinity verwendet werden.
Die Gesamtzahl der in der Affinitätsmaske festgelegten Bits gibt die Anzahl der aktiven Prozessoren im System an. Treiber sollten nicht davon ausgehen, dass alle festgelegten Bits in der Maske zusammenhängend sind, da die Prozessoren in den zukünftigen Versionen des Betriebssystems möglicherweise nicht nacheinander nummeriert werden. Die Anzahl der Prozessoren auf einem SMP-Computer ist ein nullbasierter Wert.
Wenn Ihr Treiber Daten pro Prozessor verwaltet, können Sie die KeQueryGroupAffinity-Funktion verwenden, um Cachezeilenkonflikte zu reduzieren.
Vermeiden von falscher Freigabe
Die falsche Freigabe tritt auf, wenn Prozessoren freigegebene Variablen anfordern, die voneinander unabhängig sind. Da sich die Variablen jedoch in derselben Cachezeile befinden, werden sie von den Prozessoren gemeinsam verwendet. In solchen Situationen wird die Cachezeile zwischen Prozessoren für jeden Zugriff auf die darin enthaltenen Variablen hin- und hergewechselt, was zu einer Zunahme der Cacheleerungen und Neuladevorgänge führt. Dies erhöht die Systembusauslastung und verringert die Gesamtleistung des Systems.
Um eine falsche Freigabe zu vermeiden, richten Sie wichtige Datenstrukturen (z. B. Spinsperren, Pufferwarteschlangenheader, singly verknüpfte Listen) mithilfe von NdisGetSharedDataAlignment an Cachezeilengrenzen aus.
Ordnungsgemäßes Verwenden von Sperrmechanismen
Spinsperren können die Leistung beeinträchtigen, wenn sie nicht ordnungsgemäß verwendet werden. Treiber sollten die Verwendung von Drehsperren minimieren, indem sie nach Möglichkeit verriegelte Vorgänge verwenden. In einigen Fällen kann jedoch eine Drehsperre für einige Zwecke die beste Wahl sein. Wenn z. B. ein Treiber eine Drehsperre erhält, während er die Verweisanzahl für die Anzahl der Pakete verarbeitet, die nicht an den Treiber zurückgegeben wurden, ist es nicht erforderlich, einen ineinandergreifenden Vorgang zu verwenden. Weitere Informationen finden Sie unter Synchronisierung und Benachrichtigung in Netzwerktreibern.
Hier finden Sie einige Tipps für die effektive Verwendung von Sperrmechanismen:
Verwenden Sie NDIS-Listenfunktionen wie die folgenden für die Verwaltung von Ressourcenpools:
Wenn Sie Drehsperren verwenden müssen, verwenden Sie sie nur zum Schutz von Daten, nicht zum Code. Verwenden Sie nicht eine Sperre, um alle Daten zu schützen, die in gemeinsamen Pfaden verwendet werden. Trennen Sie beispielsweise die in den Sende- und Empfangspfaden verwendeten Daten in zwei Datenstrukturen, sodass der Empfangspfad nicht betroffen ist, wenn der Sendepfad seine Daten sperren muss.
Wenn Sie Drehsperren verwenden und sich der Pfad bereits auf DPC-Ebene befindet, verwenden Sie die Funktionen NdisDprAcquireSpinLock und NdisDprReleaseSpinLock , um zusätzlichen Code beim Abrufen und Freigeben der Sperren zu vermeiden.
Verwenden Sie die folgenden NDIS-RWLock-Funktionen, um die Anzahl der Abrufe und Freigaben der Drehsperre zu minimieren:
Verwenden von 64-Bit-DMA
64-Bit-DMA Wenn der Netzwerkadapter 64-Bit-DMA unterstützt, müssen Schritte ausgeführt werden, um zusätzliche Kopien für Adressen oberhalb des 4-GB-Bereichs zu vermeiden. Wenn der Treiber NdisMRegisterScatterGatherDma aufruft, muss das flag NDIS_SG_DMA_64_BIT_ADDRESS im Flags-Parameter festgelegt werden.
Sicherstellen einer ordnungsgemäßen Pufferausrichtung
Die Pufferausrichtung an einer Cachezeilengrenze verbessert die Leistung beim Kopieren von Daten aus einem Puffer in einen anderen. Die meisten Netzwerkadapter-Empfangspuffer sind ordnungsgemäß ausgerichtet, wenn sie zum ersten Mal zugeordnet werden, aber die Benutzerdaten, die schließlich in den Anwendungspuffer kopiert werden müssen, sind aufgrund des belegten Headerspeichers falsch ausgerichtet. Im Fall von TCP-Daten (das häufigste Szenario) führt die Verschiebung aufgrund der TCP-, IP- und Ethernet-Header zu einer Verschiebung von 0x36 Bytes. Um dieses Problem zu beheben, empfehlen wir, dass Treiber einen etwas größeren Puffer zuordnen und Paketdaten mit einem Offset von 0xA Bytes einfügen. Dadurch wird sichergestellt, dass die Benutzerdaten ordnungsgemäß ausgerichtet werden, nachdem die Puffer um 0x36 Bytes für den Header verschoben wurden. Weitere Informationen zu Cacheliniengrenzen finden Sie im Abschnitt Hinweise zu NdisMAllocateSharedMemory.
Verwenden von Scatter-Gather DMA
NDIS Scatter/Gather DMA bietet der Hardware Unterstützung für die Übertragung von Daten in und aus nicht zusammenhängenden Bereichen des physischen Speichers. Scatter-Gather DMA verwendet eine SCATTER_GATHER_LIST-Struktur , die ein Array von SCATTER_GATHER_ELEMENT Strukturen und die Anzahl der Elemente im Array enthält. Diese Struktur wird aus dem Paketdeskriptor abgerufen, der an die Sendefunktion des Treibers übergeben wird. Jedes Element des Arrays stellt die Länge und die Anfangsadresse eines physisch zusammenhängenden Scatter-Gather Bereichs bereit. Der Treiber verwendet die Längen- und Adressinformationen für die Übertragung der Daten.
Die Verwendung der Scatter-Gather Routinen für DMA-Vorgänge kann die Nutzung von Systemressourcen verbessern, indem diese Ressourcen nicht statisch gesperrt werden, wie es bei der Verwendung von Zuordnungsregistern der Grund wäre. Weitere Informationen finden Sie unter NDIS Scatter/Gather DMA.
Wenn der Netzwerkadapter tcp Segmentation Offload (Large Send Offload) unterstützt, muss der Treiber die maximale Puffergröße, die er von TCP/IP abrufen kann, in den Parameter MaximumPhysicalMapping innerhalb der NdisMRegisterScatterGatherDma-Funktion übergeben. Dadurch wird sichergestellt, dass der Treiber über genügend Zuordnungsregister verfügt, um die Scatter-Gather Liste zu erstellen und mögliche Pufferzuordnungen und Kopiervorgänge auszuschließen. Weitere Informationen finden Sie in den folgenden Themen:
Unterstützung der empfangsseitigen Drosselung
Um Unterbrechungen bei der Medienwiedergabe in Multimediaanwendungen zu minimieren, müssen Treiber von NDIS 6.20 und höher empfangsseitige Drosselung (Receive Side Throttle, RST) bei der Verarbeitung von Empfangsunterbrechungen unterstützen. Weitere Informationen finden Sie unter
Empfangsseitige Drosselung in NDIS 6.20 "Senden und Empfangen von Codepfaden" in der Zusammenfassung der änderungen, die zum Portieren eines Miniporttreibers zu NDIS 6.20 erforderlich sind