Problemi di prestazioni per un driver Miniport WavePci

L'impatto delle prestazioni di un driver audio sul sistema può essere notevolmente ridotto seguendo questi principi generali:

  • Ridurre al minimo il codice eseguito durante la normale operazione.

  • Eseguire il codice solo quando necessario.

  • Prendere in considerazione il consumo totale delle risorse di sistema (non solo il caricamento della CPU).

  • Ottimizzare il codice per velocità e dimensioni.

Inoltre, i driver miniport WavePci devono risolvere diversi problemi di prestazioni specifici per i dispositivi audio. La discussione seguente riguarda principalmente i problemi di rendering audio, anche se alcune delle tecniche suggerite si applicano anche all'acquisizione audio.

Meccanismi di manutenzione del flusso

Prima di discutere le ottimizzazioni delle prestazioni, è necessario comprendere i meccanismi WavePci per i flussi di manutenzione.

Quando si elabora un rendering o un flusso di acquisizione dell'onda, un dispositivo audio richiede la manutenzione a intervalli regolari dal driver miniport. Quando sono disponibili nuovi mapping per un flusso, il driver aggiunge tali mapping alla coda DMA del flusso. Il driver rimuove anche dalla coda eventuali mapping già elaborati. Per informazioni sui mapping, vedere WavePci Latency.

Per eseguire la manutenzione, il driver miniport fornisce una chiamata di procedura posticipata (DPC) o una routine di servizio di interruzione (ISR), a seconda che l'intervallo sia impostato da un timer di sistema o da interruzioni guidate da DMA. In quest'ultimo caso, l'hardware DMA attiva in genere un interruzione ogni volta che termina il trasferimento di alcuni dati di flusso.

Ogni volta che il DPC o ISR viene eseguito, determina quali flussi richiedono la manutenzione. Il DPC o ISR esegue un flusso chiamando il metodo IPortWavePci::Notify . Questo metodo accetta come parametro di chiamata il gruppo di servizi del flusso, che è un oggetto di tipo IServiceGroup. Il metodo Notify chiama il metodo RequestService del gruppo di servizi (vedere IServiceSink::RequestService).

Un oggetto service-group contiene un gruppo di sink di servizio, che sono oggetti di tipo IServiceSink. IServiceGroup è derivato da IServiceSink e entrambe le interfacce hanno metodi RequestService . Quando il metodo Notify chiama il metodo RequestService del gruppo di servizi, il gruppo di servizi risponde chiamando il metodo RequestService in ogni sink del servizio nel gruppo.

Il gruppo di servizi di un flusso contiene almeno un sink di servizio, che il driver della porta aggiunge al gruppo di servizi immediatamente dopo la creazione del flusso. Il driver di porta chiama il metodo IMiniportWavePci::NewStream per ottenere un puntatore al gruppo di servizi. Il metodo RequestService del sink di servizio è la routine di servizio specifica del driver di porta. Questa routine esegue le operazioni seguenti:

Come illustrato in Eventi KS, i client possono essere registrati per ricevere una notifica quando un flusso raggiunge una determinata posizione o quando un orologio raggiunge un determinato timestamp. Il metodo NewStream ha la possibilità di non fornire un gruppo di servizi, nel qual caso il driver di porta imposta il proprio timer per contrassegnare gli intervalli tra le chiamate alla routine del servizio.

Come il metodo NewStream , il metodo IMiniportWavePci::Init restituisce anche un puntatore a un gruppo di servizi. Dopo la chiamata a Init , il driver di porta aggiunge il sink di servizio al gruppo di servizi. Questo particolare sink di servizio contiene la routine di servizio per l'intero filtro. Il paragrafo precedente descrive il sink del servizio per il flusso associato a un pin nel filtro. Questa routine di servizio chiama il metodo IMiniportWavePci::Service del driver miniport . La routine del servizio viene eseguita ogni volta che il DPC o ISR chiama Notifica con il gruppo di servizi per il filtro. Il metodo Init ha la possibilità di non fornire un gruppo di servizi, nel qual caso il driver della porta non chiama mai la routine del servizio di filtro.

Interruzioni hardware

Alcuni driver miniport generano troppe interruzioni hardware o meno sufficienti. In alcuni dispositivi di rendering WavePci con accelerazione hardware DirectSound si verifica un interruzione hardware solo quando la fornitura di mapping è quasi esaurita e il motore di rendering è a rischio di fame. In altri dispositivi WavePci con accelerazione hardware, un interruzione hardware si verifica su ogni singolo completamento del mapping o su un altro intervallo relativamente piccolo. In questo caso, l'ISR rileva spesso che ha poco a che fare, ma ogni interruzione usa ancora risorse di sistema con scambi di registrazione e ricarica della cache. Il primo passaggio per migliorare le prestazioni del driver consiste nel ridurre il numero di interruzioni il più possibile senza rischiare la fame. Dopo aver eliminato gli interruzioni non necessari, è possibile ottenere miglioramenti aggiuntivi delle prestazioni progettando l'ISR per eseguire in modo più efficiente.

In alcuni driver, gli ISR perdono tempo chiamando il metodo Notify di un flusso ogni volta che si verifica un interruzione hardware, indipendentemente dal fatto che il flusso sia effettivamente in esecuzione. Se non si trovano flussi nello stato RUN, DMA è inattivo e qualsiasi tempo trascorso cercando di acquisire mapping, mapping di rilascio o verificare la presenza di nuovi eventi in tutti i flussi viene sprecato. In un driver efficiente, ISR verifica che un flusso sia in esecuzione prima di chiamare il metodo Notify del flusso.

Tuttavia, un driver con questo tipo di ISR deve assicurarsi che tutti gli eventi in sospeso in un flusso vengano attivati quando il flusso esce dallo stato RUN. In caso contrario, gli eventi potrebbero essere ritardati o persi. Questo problema si verifica solo durante le transizioni RUN-to-PAUSE nei sistemi operativi precedenti a Microsoft Windows XP. In Windows XP e versioni successive il driver di porta segnala automaticamente eventuali eventi di posizione in sospeso immediatamente quando un flusso cambia lo stato da RUN a PAUSE. Nei sistemi operativi meno recenti, tuttavia, il driver miniport è responsabile dell'attivazione di eventuali eventi in sospeso eseguendo una chiamata finale a Notifica immediatamente dopo la sospensione del flusso. Per altre informazioni, vedere Ottimizzazione PAUSE/ACQUIRE di seguito.

Un tipico driver miniport WavePci gestisce un singolo flusso di riproduzione dal driver di sistema KMixer. L'implementazione corrente di KMixer usa almeno tre ip di mapping per bufferare un flusso di riproduzione. Ogni IRP contiene un'archiviazione di buffer sufficiente per circa 10 millisecondi di audio. Se il driver miniport attiva un interruzione hardware ogni volta che il controller DMA termina con il mapping finale in un'IRP, gli interruzioni devono verificarsi a intervalli di 10 millisecondi abbastanza regolari, che è abbastanza frequente per mantenere la coda DMA da morire.

DPC timer

Se un driver gestisce tutti i flussi DirectSound accelerati dall'hardware, deve usare un DPC timer (vedere Oggetti timer e DPC) anziché interruzioni hardware basate su DMA. Analogamente, un dispositivo WavePci su una scheda PCI con un timer su scheda può usare un interruzione hardware basata su timer anziché un DPC.

Nel caso di un buffer DirectSound, l'intero buffer può essere collegato a un singolo IRP. Se il buffer è grande e il driver miniport pianifica un interruzione hardware solo quando raggiunge la fine del buffer, le interruzioni successive possono verificarsi finora a parte che la coda DMA starve. Inoltre, se il driver gestisce un numero elevato di flussi DirectSound accelerati dall'hardware e ogni flusso genera i propri interruzioni, l'impatto cumulativo di tutti gli interruzioni può ridurre le prestazioni del sistema. In queste circostanze, il driver miniport deve evitare l'uso di interruzioni hardware per pianificare la manutenzione dei singoli flussi. Deve invece eseguire tutti i flussi in un singolo DPC pianificato per l'esecuzione a intervalli regolari generati dal timer.

Impostando l'intervallo di timer su 10 millisecondi, l'intervallo tra esecuzioni DPC successive è simile a quello descritto in precedenza per l'interruzione hardware nel caso di un singolo flusso di riproduzione KMixer. Pertanto, il DPC può gestire il flusso di riproduzione KMixer oltre ai flussi DirectSound accelerati dall'hardware.

Quando l'ultimo flusso esce dallo stato RUN, il driver miniport deve disabilitare il DPC timer per evitare di sprecare cicli di CPU del sistema. Immediatamente dopo la disabilitazione del DPC, il driver deve assicurarsi che tutti gli eventi di orologio o posizione in sospeso nei flussi in esecuzione in precedenza vengano scaricati. In Windows 98/Me e Windows 2000 il driver deve chiamare Notifica per attivare eventuali eventi in sospeso nei flussi in pausa. In Windows XP e versioni successive il sistema operativo attiva automaticamente gli eventi in sospeso quando un flusso esce dallo stato RUN, senza richiedere intervento dal driver miniport.

Ottimizzazioni PAUSE/ACQUIRE

In Windows 98/Me e Windows 2000 la routine del servizio di flusso del driver di porta WavePci (metodo RequestService ) genera sempre una chiamata al metodo IMiniportWavePciStream::Service del driver miniport , indipendentemente dal fatto che il flusso si trova nello stato RUN. In questi sistemi operativi il metodo Service deve verificare se il flusso è in esecuzione prima di dedicare tempo effettivo. Tuttavia, se il DPC o l'ISR del driver miniport è già stato ottimizzato per chiamare Notifica solo per i flussi in esecuzione, l'aggiunta di questo controllo al metodo service potrebbe essere ridondante.

In Windows XP e versioni successive, questa ottimizzazione non è necessaria perché il metodo Notify chiama il metodo Service solo nei flussi in esecuzione.

Uso dell'interfaccia IPreFetchOffset

Gli utenti DirectSound hanno familiarità con i due concetti del cursore di riproduzione e il cursore di scrittura. Il cursore di riproduzione indica la posizione nel flusso dei dati generati dal dispositivo (la stima migliore del driver attualmente in corrispondenza dell'applicazione livello dati). La posizione di scrittura è la posizione nel flusso del posto sicuro successivo per il client per scrivere dati aggiuntivi. Per WavePci, il presupposto predefinito è che il cursore di scrittura viene posizionato alla fine dell'ultimo mapping richiesto. Se il driver miniport ha acquisito un numero elevato di mapping in sospeso, l'offset tra il cursore di riproduzione e il cursore di scrittura può essere molto grande, abbastanza grande per non riuscire determinati test di posizione audio WHQL. In Windows XP e versioni successive l'interfaccia IPreFetchOffset risolve questi problemi.

Il driver miniport usa IPreFetchOffset per specificare le caratteristiche di prefetch dell'hardware del bus, che sono in gran parte determinate dalle dimensioni FIFO hardware. Il sottosistema audio usa questi dati per impostare un offset costante tra il cursore di riproduzione e il cursore di scrittura. Questo offset costante, che può essere significativamente inferiore all'offset predefinito, sfrutta il fatto che i dati possono essere scritti in un mapping anche dopo la consegna del mapping all'hardware, purché il cursore di riproduzione sia abbastanza lontano dalla posizione in cui vengono scritti i dati. Questa istruzione presuppone che il driver non copia o manipola i dati nei mapping. Un offset tipico potrebbe essere sull'ordine di 64 campioni, a seconda della progettazione del motore. Con un offset ridotto, un driver WavePci può essere completamente reattivo e funzionale, pur richiedendo un numero elevato di mapping.

Si noti che DirectSound attualmente esegue il pad del cursore di scrittura di un pin accelerato hardware di 10 millisecondi.

Per altre informazioni, vedere Offset di prefetch.

Elaborazione dei dati nei mapping

Evitare di avere il driver hardware toccare i dati nei mapping se possibile. Qualsiasi elaborazione software dei dati contenuti nei mapping deve essere suddivisa in un filtro software separato dal driver hardware. L'esecuzione di un driver hardware riduce l'efficienza e crea problemi di latenza.

Un driver hardware deve essere trasparente sulle sue reali funzionalità hardware. Il driver non deve mai dichiarare di fornire supporto hardware per una trasformazione dei dati effettivamente eseguita nel software.

Primitive di sincronizzazione

Un driver è meno probabile che si verifichino problemi di deadlock o prestazioni ora e in futuro se il relativo codice è progettato per evitare di essere bloccato ogni volta che possibile. In particolare, il thread di esecuzione di un driver deve tentare di eseguire al completamento senza il rischio di stallare durante l'attesa di un altro thread o risorsa. Ad esempio, i thread driver possono usare le funzioniXxx interlocked (ad esempio, vedere InterlockedIncrement) per coordinare gli accessi a determinate risorse condivise senza il rischio di essere bloccati.

Anche se queste sono tecniche potenti, potrebbe non essere possibile rimuovere in modo sicuro tutti i blocchi di spin, i mutex e altre primitive di sincronizzazione bloccanti dal percorso di esecuzione. Usare le funzioniXxx interlocked in modo judicioso, con la conoscenza che un'attesa indefinita potrebbe causare la starvazione dei dati.

Soprattutto, non creare primitive di sincronizzazione personalizzate. Le primitive windows predefinite (mutex, blocchi di spin e così via) sono probabilmente modificate in base alle esigenze per supportare nuove funzionalità dell'utilità di pianificazione in futuro e un driver che usa costrutti personalizzati è virtualmente garantito non funzionare in futuro.

Interfaccia IPinCount

In Windows XP e versioni successive, l'interfaccia IPinCount consente a un driver miniport di tenere conto in modo più accurato delle risorse hardware usate allocando un pin. Chiamando il metodo IPinCount::P inCount del driver miniport, il driver della porta esegue le operazioni seguenti:

  • Espone i conteggi correnti del pin del filtro (come gestito dal driver della porta) al driver miniport.

  • Offre al driver miniport l'opportunità di rivedere i conteggi dei pin per riflettere dinamicamente la disponibilità corrente delle risorse hardware.

Per alcuni dispositivi audio, i flussi d'onda con attributi diversi (3D, stereo/mono e così via) potrebbero avere anche pesi diversi in termini di quanti risorse hardware utilizzano. Quando si apre o chiude un flusso "leggero", il driver incrementa o decrementa il numero di pin disponibili per uno. Quando si apre un flusso "heavyweight", tuttavia, il driver miniport potrebbe dover decrerere il conteggio dei pin disponibili per due anziché per uno per indicare in modo più accurato il numero di pin che possono essere creati con le risorse rimanenti.

Il processo viene invertito quando viene chiuso un flusso pesante. Il conteggio dei pin disponibili può aumentare di più di uno per riflettere il fatto che due o più flussi leggeri possono essere creati dalle risorse appena liberate.