Freigeben über


Leistungsprobleme für einen WavePci Miniport-Treiber

Die Leistungseinbußen eines Audiotreibers auf das System können durch die Folgenden allgemeinen Prinzipien erheblich verringert werden:

  • Minimieren Sie den Code, der während des normalen Betriebs ausgeführt wird.

  • Führen Sie Code nur bei Bedarf aus.

  • Betrachten Sie den Gesamtverbrauch der Systemressourcen (nicht nur die CPU-Auslastung).

  • Optimieren Sie Code für Geschwindigkeit und Größe.

Darüber hinaus müssen WavePci-Miniporttreiber mehrere Leistungsprobleme beheben, die für Audiogeräte spezifisch sind. Die folgende Diskussion behandelt in erster Linie Probleme mit dem Audiorendering, obwohl einige der vorgeschlagenen Techniken auch für die Audioaufnahme gelten.

Streamwartungsmechanismen

Bevor Sie Leistungsoptimierungen besprechen, ist ein Hintergrund erforderlich, um die WavePci-Mechanismen für die Wartung von Streams zu verstehen.

Bei der Verarbeitung eines Wellenrenders oder Aufzeichnungsdatenstroms muss ein Audiogerät in regelmäßigen Abständen vom Miniporttreiber gewartet werden. Wenn neue Zuordnungen für einen Stream verfügbar sind, fügt der Treiber diese Zuordnungen der DMA-Warteschlange des Streams hinzu. Der Treiber entfernt auch alle Zuordnungen, die bereits verarbeitet wurden, aus der Warteschlange. Informationen zu Zuordnungen finden Sie unter WavePci Latency.

Um die Wartung auszuführen, stellt der Miniporttreiber entweder einen verzögerten Prozeduraufruf (DPC) oder interrupt Service Routine (ISR) bereit, je nachdem, ob das Intervall von einem Systemtimer oder von DMA-gesteuerten Interrupts festgelegt wird. Im letzteren Fall löst die DMA-Hardware in der Regel jedes Mal einen Interrupt aus, wenn die Übertragung einer bestimmten Datenstrommenge abgeschlossen ist.

Jedes Mal, wenn der DPC oder ISR ausgeführt wird, bestimmt er, welche Streams gewartet werden müssen. Die DPC- oder ISR-Dienste einen Stream durch Aufrufen der IPortWavePci::Notify-Methode . Diese Methode verwendet als Aufrufparameter die Dienstgruppe des Datenstroms, die ein Objekt vom Typ IServiceGroup ist. Die Notify-Methode ruft die RequestService-Methode der Dienstgruppe auf (siehe IServiceSink::RequestService).

Ein Dienstgruppenobjekt enthält eine Gruppe von Dienstsenken, bei denen es sich um Objekte vom Typ IServiceSink handelt. IServiceGroup wird von IServiceSink abgeleitet, und beide Schnittstellen verfügen über RequestService-Methoden . Wenn die Notify-Methode die RequestService-Methode der Dienstgruppe aufruft, antwortet die Dienstgruppe, indem sie die RequestService-Methode für jede Dienstsenke in der Gruppe aufruft.

Die Dienstgruppe eines Datenstroms enthält mindestens eine Dienstsenke, die der Porttreiber unmittelbar nach der Erstellung des Datenstroms der Dienstgruppe hinzufügt. Der Porttreiber ruft die IMiniportWavePci::NewStream-Methode des Miniporttreibers auf, um einen Zeiger auf die Dienstgruppe abzurufen. Die RequestService-Methode der Dienstsenke ist die streamspezifische Dienstroutine des Porttreibers. Diese Routine führt Folgendes aus:

Wie in KS-Ereignisse erläutert, können Sich Clients registrieren, um benachrichtigt zu werden, wenn ein Stream eine bestimmte Position erreicht oder wenn eine Uhr einen bestimmten Zeitstempel erreicht. Die NewStream-Methode hat die Möglichkeit, keine Dienstgruppe anzugeben. In diesem Fall richtet der Porttreiber einen eigenen Timer ein, um die Intervalle zwischen Aufrufen seiner Dienstroutine zu markieren.

Wie die NewStream-Methode gibt auch die IMiniportWavePci::Init-Methode des Miniporttreibers einen Zeiger auf eine Dienstgruppe aus. Nach dem Init-Aufruf fügt der Porttreiber der Dienstgruppe seine Dienstsenke hinzu. Diese bestimmte Dienstsenke enthält die Dienstroutine für den Filter als Ganzes. (Im vorherigen Absatz wird die Dienstsenke für den Stream beschrieben, der einem Pin am Filter zugeordnet ist.) Diese Dienstroutine ruft die IMiniportWavePci::Service-Methode des Miniporttreibers auf. Die Dienstroutine wird jedes Mal ausgeführt, wenn der DPC oder ISR die Benachrichtigung mit der Dienstgruppe für den Filter aufruft. Die Init-Methode hat die Möglichkeit, keine Dienstgruppe anzugeben. In diesem Fall ruft der Porttreiber nie seine Filterdienstroutine auf.

Hardwareunterbrechungen

Einige Miniporttreiber generieren entweder zu viele oder nicht genügend Hardwareunterbrechungen. Bei einigen WavePci-Renderinggeräten mit DirectSound-Hardwarebeschleunigung tritt ein Hardware-Interrupt nur auf, wenn die Bereitstellung von Zuordnungen nahezu erschöpft ist und die Rendering-Engine vom Hunger bedroht ist. Bei anderen hardwarebeschleunigten WavePci-Geräten tritt bei jeder einzelnen Zuordnungsvervollständigung oder einem anderen relativ kleinen Intervall ein Hardware-Interrupt auf. In diesem Fall stellt der ISR häufig fest, dass er wenig zu tun hat, aber jeder Interrupt verbraucht weiterhin Systemressourcen mit Register swaps und Cache reloads. Der erste Schritt zur Verbesserung der Treiberleistung besteht darin, die Anzahl der Unterbrechungen so weit wie möglich zu reduzieren, ohne den Hunger zu riskieren. Nachdem unnötige Unterbrechungen beseitigt wurden, können zusätzliche Leistungsgewinne erzielt werden, indem die ISR für eine effizientere Ausführung entworfen wird.

In einigen Treibern verschwenden ISRs Zeit, indem sie die Notify-Methode eines Datenstroms jedes Mal aufrufen, wenn eine Hardwareunterbrechung auftritt – unabhängig davon, ob der Stream tatsächlich ausgeführt wird. Wenn sich keine Streams im RUN-Zustand befinden, ist DMA inaktiv, und die Zeit, die für das Abrufen von Zuordnungen, das Freigeben von Zuordnungen oder die Suche nach neuen Ereignissen in streams aufgewendet wird, wird verschwendet. In einem effizienten Treiber überprüft der ISR, ob ein Stream ausgeführt wird, bevor die Notify-Methode des Datenstroms aufgerufen wird.

Ein Treiber mit diesem Typ von ISR muss jedoch sicherstellen, dass alle ausstehenden Ereignisse für einen Stream ausgelöst werden, wenn der Stream den RUN-Status beendet. Andernfalls können die Ereignisse verzögert oder verloren gegangen sein. Dieses Problem tritt nur bei RUN-to-PAUSE-Übergängen in Betriebssystemen auf, die älter als Microsoft Windows XP sind. In Windows XP und höher signalisiert der Porttreiber automatisch alle ausstehenden Positionsereignisse sofort, wenn ein Stream den Status von RUN in PAUSE ändert. In den älteren Betriebssystemen ist der Miniporttreiber jedoch dafür verantwortlich, alle ausstehenden Ereignisse auszulösen, indem er unmittelbar nach dem Angehalten des Datenstroms einen letzten Aufruf von Notify auslöst. Weitere Informationen finden Sie weiter unten unter PAUSE/ACQUIRE-Optimierungen.

Ein typischer WavePci-Miniporttreiber verwaltet einen einzelnen Wiedergabestream aus dem KMixer-Systemtreiber. Die aktuelle Implementierung von KMixer verwendet mindestens drei Zuordnungs-IRPs, um einen Wiedergabestream zu puffern. Jede IRP enthält genügend Pufferspeicher für ca. 10 Millisekunden Audio. Wenn der Miniporttreiber jedes Mal, wenn der DMA-Controller die endgültige Zuordnung in einem IRP beendet, einen Hardware-Interrupt auslöst, sollten Unterbrechungen in relativ regelmäßigen 10-Millisekundenintervallen auftreten, was häufig genug ist, um das Aushungen der DMA-Warteschlange zu verhindern.

Timer-DPCs

Wenn ein Treiber hardwarebeschleunigte DirectSound-Streams verwaltet, sollte er anstelle von DMA-gesteuerten Hardwareunterbrechungen einen Timer-DPC (siehe Timer-Objekte und DPCs) verwenden. Entsprechend kann ein WavePci-Gerät auf einem PCI-Karte mit einem integrierten Timer einen timergesteuerten Hardware-Interrupt anstelle eines DPC verwenden.

Bei einem DirectSound-Puffer kann der gesamte Puffer an eine einzelne IRP angefügt werden. Wenn der Puffer groß ist und der Miniporttreiber einen Hardware-Interrupt nur dann plant, wenn er das Ende des Puffers erreicht, können aufeinander folgende Unterbrechungen so weit voneinander entfernt sein, dass die DMA-Warteschlange verhungern kann. Wenn der Treiber außerdem eine große Anzahl von hardwarebeschleunigten DirectSound-Streams verwaltet und jeder Stream eigene Interrupts generiert, kann die kumulative Auswirkung aller Interrupts die Systemleistung beeinträchtigen. Unter diesen Umständen sollte der Miniporttreiber die Verwendung von Hardwareunterbrechungen vermeiden, um die Wartung einzelner Streams zu planen. Stattdessen sollten alle Datenströme in einem einzelnen DPC gewartet werden, der für die Ausführung in regelmäßig vom Timer generierten Intervallen geplant ist.

Durch Festlegen des Timerintervalls auf 10 Millisekunden ähnelt das Intervall zwischen aufeinanderfolgenden DPC-Ausführungen dem zuvor für den Hardware-Interrupt im Fall eines einzelnen KMixer-Wiedergabestreams beschriebenen. So kann der DPC den KMixer-Wiedergabestream zusätzlich zu hardwarebeschleunigten DirectSound-Streams verarbeiten.

Wenn der letzte Stream den RUN-Zustand beendet, sollte der Miniporttreiber den Timer-DPC deaktivieren, um zu vermeiden, dass System-CPU-Zyklen verschwendet werden. Unmittelbar nach dem Deaktivieren des DPC sollte der Treiber sicherstellen, dass alle Uhr- oder Positionsereignisse, die für zuvor ausgeführte Streams ausstehen, geleert werden. In Windows 98/Me und Windows 2000 sollte der Treiber Notify aufrufen, um alle ausstehenden Ereignisse für die Streams auszulösen, die angehalten werden. In Windows XP und höher löst das Betriebssystem alle ausstehenden Ereignisse automatisch aus, wenn ein Stream den RUN-Zustand beendet, ohne dass der Miniporttreiber eingreifen muss.

PAUSE/ACQUIRE-Optimierungen

In Windows 98/Me und Windows 2000 generiert die Streamdienstroutine des WavePci-Porttreibers ( die RequestService-Methode ) immer einen Aufruf der IMiniportWavePciStream::Service-Methode des Miniporttreibers , unabhängig davon, ob sich der Stream im RUN-Zustand befindet. In diesen Betriebssystemen sollte die Dienstmethode überprüfen, ob der Stream ausgeführt wird, bevor sie Zeit mit der eigentlichen Arbeit aufwendet. (Wenn der DPC oder ISR des Miniporttreibers jedoch bereits so optimiert wurde, dass die Benachrichtigung nur für ausgeführte Streams aufgerufen wird, kann das Hinzufügen dieser Überprüfung zur Service-Methode redundant sein.)

In Windows XP und höher ist diese Optimierung nicht erforderlich, da die Notify-Methode die Service-Methode nur für streams aufruft, die ausgeführt werden.

Verwenden der IPreFetchOffset-Schnittstelle

DirectSound-Benutzer sind mit den dualen Konzepten des Wiedergabecursors und des Schreibcursors vertraut. Der Wiedergabecursor gibt die Position im Datenstrom an, die vom Gerät ausgegeben werden (die beste Schätzung der Stichprobe, die der Treiber derzeit beim DAC hat). Die Schreibposition ist die Position im Stream der nächsten sicheren Stelle, an der der Client zusätzliche Daten schreiben kann. Bei WavePci wird standardmäßig davon ausgegangen, dass der Schreibcursor am Ende der letzten angeforderten Zuordnung positioniert wird. Wenn der Miniporttreiber eine große Anzahl ausstehender Zuordnungen erworben hat, kann der Offset zwischen dem Wiedergabecursor und dem Schreibcursor sehr groß sein – groß genug, um bestimmte WHQL-Audiopositionstests zu bestehen. In Windows XP und höher behebt die IPreFetchOffset-Schnittstelle diese Probleme.

Der Miniporttreiber verwendet IPreFetchOffset, um die Prefetch-Eigenschaften der Bus-master Hardware anzugeben, die größtenteils von der FIFO-Größe der Hardware bestimmt werden. Das Audiosubsystem verwendet diese Daten, um einen konstanten Offset zwischen dem Wiedergabecursor und dem Schreibcursor festzulegen. Dieser konstante Offset, der deutlich kleiner als der Standardoffset sein kann, nutzt die Tatsache, dass Daten auch nach der Übergabe der Zuordnung an die Hardware in eine Zuordnung geschrieben werden können, solange der Wiedergabecursor weit genug von der Position entfernt ist, in die die Daten geschrieben werden. (Bei dieser Anweisung wird davon ausgegangen, dass der Treiber die Daten in Zuordnungen nicht kopiert oder anderweitig bearbeitet.) Ein typischer Offset kann je nach Motorentwurf in der Größenordnung von 64 Beispielen angegeben werden. Mit einem Offset dieses kleinen Offsets kann ein WavePci-Treiber vollständig reaktionsfähig und funktionsfähig sein und gleichzeitig eine große Anzahl von Zuordnungen anfordern.

Beachten Sie, dass DirectSound den Schreibcursor eines hardwarebeschleunigten Pins derzeit um 10 Millisekunden auffüllt.

Weitere Informationen finden Sie unter Prefetch Offsets.

Verarbeiten von Daten in Zuordnungen

Vermeiden Sie es, dass Ihr Hardwaretreiber die Daten in den Zuordnungen berührt, wenn dies möglich ist. Jede Softwareverarbeitung von Daten, die in Zuordnungen enthalten sind, sollte in einen vom Hardwaretreiber getrennten Softwarefilter aufgeteilt werden. Wenn ein Hardwaretreiber eine solche Verarbeitung ausführt, verringert sich die Effizienz und verursacht Latenzprobleme.

Ein Hardwaretreiber sollte versuchen, seine tatsächlichen Hardwarefunktionen transparent zu machen. Der Treiber sollte niemals behaupten, Hardwareunterstützung für eine Datentransformation bereitzustellen, die tatsächlich in Software ausgeführt wird.

Synchronisierungsgrundtypen

Ein Treiber hat jetzt und in Zukunft weniger Deadlock- oder Leistungsprobleme, wenn sein Code so konzipiert ist, dass er möglichst nicht blockiert wird. Insbesondere sollte der Ausführungsthread eines Treibers versuchen, bis zum Abschluss auszuführen, ohne das Risiko zu riskieren, dass er angehalten wird, während auf einen anderen Thread oder eine andere Ressource gewartet wird. Beispielsweise können Treiberthreads die interlockedXxx-Funktionen (z. B. unter InterlockedIncrement) verwenden, um ihre Zugriffe auf bestimmte freigegebene Ressourcen zu koordinieren, ohne dass das Risiko besteht, blockiert zu werden.

Obwohl dies leistungsstarke Techniken sind, können Sie möglicherweise nicht alle Drehsperren, Mutexe und andere blockierende Synchronisierungsgrundtypen sicher aus dem Ausführungspfad entfernen. Verwenden Sie die interlockedXxx-Funktionen mit Bedacht, mit dem Wissen, dass eine unbegrenzte Wartezeit zu Datenmangel führen kann.

Erstellen Sie vor allem keine benutzerdefinierten Synchronisierungsgrundtypen. Die integrierten Windows-Grundtypen (Mutexes, Drehsperren usw.) werden wahrscheinlich nach Bedarf geändert, um neue Schedulerfeatures in Der Zukunft zu unterstützen, und ein Treiber, der benutzerdefinierte Konstrukte verwendet, funktioniert in Zukunft praktisch nicht mehr.

IPinCount-Schnittstelle

In Windows XP und höher bietet die IPinCount-Schnittstelle eine Möglichkeit für einen Miniporttreiber, die Hardwareressourcen genauer zu berücksichtigen, die durch die Zuweisung eines Pins verbraucht werden. Durch Aufrufen der IPinCount::P inCount-Methode des Miniporttreibers führt der Porttreiber folgendes aus:

  • Macht die aktuelle Pinanzahl des Filters (wie vom Porttreiber verwaltet) für den Miniporttreiber verfügbar.

  • Gibt dem Miniporttreiber die Möglichkeit, die Pinanzahl zu überarbeiten, um die aktuelle Verfügbarkeit von Hardwareressourcen dynamisch widerzuspiegeln.

Bei einigen Audiogeräten können Wellenstreams mit unterschiedlichen Attributen (3D, Stereo/Mono usw.) auch unterschiedliche "Gewichtungen" hinsichtlich der Anzahl der Hardwareressourcen aufweisen, die sie verbrauchen. Beim Öffnen oder Schließen eines "Lightweight"-Streams erhöht oder verringert der Treiber die Anzahl der verfügbaren Pins um eins. Beim Öffnen eines "schwergewichtigen" Datenstroms muss der Miniporttreiber jedoch möglicherweise die anzahl verfügbarer Pins um zwei statt um eins verringern, um die Anzahl der Pins, die mit den verbleibenden Ressourcen erstellt werden können, genauer anzugeben.

Der Prozess wird umgekehrt, wenn ein schwerer Stream geschlossen wird. Die anzahl verfügbarer Pins kann um mehrere erhöht werden, um die Tatsache widerzuspiegeln, dass zwei oder mehr Lightweight-Streams aus den neu freigegebenen Ressourcen erstellt werden können.