Problemas de rendimiento de un controlador WavePci Miniport
El impacto en el rendimiento de un controlador de audio en el sistema puede reducirse significativamente siguiendo estos principios generales:
Minimice el código que se ejecuta durante el funcionamiento normal.
Ejecute código solo cuando sea necesario.
Tenga en cuenta el consumo total de recursos del sistema (no solo la carga de CPU).
Optimice el código para la velocidad y el tamaño.
Además, los controladores de miniport de WavePci deben abordar varios problemas de rendimiento específicos de los dispositivos de audio. En la siguiente discusión se tratan principalmente los problemas de representación de audio, aunque algunas de las técnicas sugeridas también se aplican a la captura de audio.
Mecanismos de mantenimiento de flujos
Antes de analizar las optimizaciones de rendimiento, es necesario conocer algunos mecanismos wavePci para las secuencias de mantenimiento.
Al procesar una representación de onda o una secuencia de captura, un dispositivo de audio requiere mantenimiento a intervalos regulares por parte del controlador de miniporte. Cuando hay nuevas asignaciones disponibles para una secuencia, el controlador agrega esas asignaciones a la cola DMA de la secuencia. El controlador también quita de la cola las asignaciones que ya se han procesado. Para obtener información sobre las asignaciones, vea WavePci Latency.
Para realizar el mantenimiento, el controlador de minipuerto proporciona una llamada a procedimiento diferido (DPC) o una rutina de servicio de interrupción (ISR), dependiendo de si el intervalo está establecido por un temporizador del sistema o por interrupciones controladas por DMA. En este último caso, el hardware DMA normalmente desencadena una interrupción cada vez si finaliza la transferencia de cierta cantidad de datos de flujo.
Cada vez que se ejecuta DPC o ISR, determina qué flujos requieren mantenimiento. El DPC o ISR ofrece un flujo mediante una llamada al método IPortWavePci::Notify . Este método toma como parámetro de llamada el grupo de servicios de la secuencia, que es un objeto de tipo IServiceGroup. El método Notify llama al método RequestService del grupo de servicios (consulte IServiceSink::RequestService).
Un objeto de grupo de servicios contiene un grupo de receptores de servicio, que son objetos de tipo IServiceSink. IServiceGroup se deriva de IServiceSink y ambas interfaces tienen métodos RequestService . Cuando el método Notify llama al método RequestService del grupo de servicios, el grupo de servicios responde llamando al método RequestService en cada receptor de servicio del grupo.
El grupo de servicios de una secuencia contiene al menos un receptor de servicio, que el controlador de puerto agrega al grupo de servicios inmediatamente después de la creación de la secuencia. El controlador de puerto llama al método IMiniportWavePci::NewStream del controlador de miniport para obtener un puntero al grupo de servicios. El método RequestService del receptor del servicio es la rutina de servicio específica del controlador de puerto. Esta rutina hace lo siguiente:
Llama al método IMiniportWavePciStream::Service del controlador de miniport.
Desencadena los eventos de posición o reloj recién pendientes en la secuencia desde la última vez que se ejecutó la rutina de servicio.
Como se describe en Eventos KS, los clientes pueden registrarse para recibir notificaciones cuando una secuencia alcanza una posición determinada o cuando un reloj alcanza una marca de tiempo determinada. El método NewStream tiene la opción de no proporcionar un grupo de servicios, en cuyo caso el controlador de puerto configura su propio temporizador para marcar los intervalos entre las llamadas a su rutina de servicio.
Al igual que el método NewStream , el método IMiniportWavePci::Init del controlador de miniport también genera un puntero a un grupo de servicios. Después de la llamada init , el controlador de puerto agrega su receptor de servicio al grupo de servicios. Este receptor de servicio determinado contiene la rutina de servicio para el filtro en su conjunto. (En el párrafo anterior se describe el receptor del servicio para la secuencia asociada a un pin en el filtro). Esta rutina de servicio llama al método IMiniportWavePci::Service del controlador de miniport. La rutina de servicio se ejecuta cada vez que el DPC o ISR llama a Notify con el grupo de servicios para el filtro. El método Init tiene la opción de no proporcionar un grupo de servicios, en cuyo caso el controlador de puerto nunca llama a su rutina de servicio de filtro.
Interrupciones de hardware
Algunos controladores de minipuerto generan demasiadas interrupciones de hardware o no suficientes. En algunos dispositivos de representación wavePci con aceleración de hardware de DirectSound, una interrupción de hardware solo se produce cuando el suministro de asignaciones está casi agotado y el motor de representación está en riesgo de hambre. En otros dispositivos WavePci acelerados por hardware, se produce una interrupción de hardware en cada finalización de asignación única o en algún otro intervalo relativamente pequeño. En este caso, el ISR encuentra con frecuencia que tiene poco que hacer, pero cada interrupción todavía consume recursos del sistema con los intercambios de registros y recargas de caché. El primer paso para mejorar el rendimiento del controlador es reducir el número de interrupciones tanto como sea posible sin arriesgarse a morir de hambre. Después de eliminar interrupciones innecesarias, se pueden lograr mejoras de rendimiento adicionales mediante el diseño del ISR para que se ejecute de forma más eficaz.
En algunos controladores, los ISR pierden tiempo llamando al método Notify de una secuencia cada vez que se produce una interrupción de hardware, independientemente de si la secuencia se está ejecutando realmente. Si no hay ningún flujo en estado RUN, DMA está inactivo y se desperdicia el tiempo dedicado a adquirir asignaciones, asignaciones de versión o comprobar si hay nuevos eventos en cualquier flujo. En un controlador eficaz, el ISR comprueba que se está ejecutando una secuencia antes de llamar al método Notify de la secuencia.
Sin embargo, un controlador con este tipo de ISR debe asegurarse de que los eventos pendientes en una secuencia se desencadenan cuando la secuencia sale del estado RUN. De lo contrario, los eventos pueden retrasarse o perderse. Este problema solo surge durante las transiciones de EJECUCIÓN a PAUSA en sistemas operativos anteriores a Microsoft Windows XP. En Windows XP y versiones posteriores, el controlador de puerto indica automáticamente los eventos de posición pendientes inmediatamente cuando una secuencia cambia el estado de RUN a PAUSE. Sin embargo, en los sistemas operativos más antiguos, el controlador de minipuerto es responsable de desencadenar los eventos pendientes mediante la realización de una llamada final a Notify inmediatamente después de pausar la secuencia. Para obtener más información, vea PAUSE/ACQUIRE Optimizations below.
Un controlador de miniport de WavePci típico administra una única secuencia de reproducción desde el controlador del sistema KMixer. La implementación actual de KMixer usa un mínimo de tres IRP de asignación para almacenar en búfer una secuencia de reproducción. Cada IRP contiene suficiente almacenamiento en búfer durante unos 10 milisegundos de audio. Si el controlador de minipuerto desencadena una interrupción de hardware cada vez que el controlador DMA finaliza con la asignación final en un IRP, las interrupciones deben producirse en intervalos bastante regulares de 10 milisegundos, lo que es lo suficientemente frecuente como para evitar que la cola de DMA deje de morir de hambre.
DPC del temporizador
Si un controlador administra los flujos directSound acelerados por hardware, debe usar un DPC del temporizador (consulte Objetos de temporizador y DPC) en lugar de interrupciones de hardware controladas por DMA. Equivalentemente, un dispositivo WavePci en una tarjeta PCI con un temporizador incorporado puede usar una interrupción de hardware controlada por temporizador en lugar de un DPC.
En el caso de un búfer de DirectSound, todo el búfer se puede conectar a un único IRP. Si el búfer es grande y el controlador de minipuerto programa una interrupción de hardware solo cuando llega al final del búfer, las interrupciones sucesivas pueden producirse tan lejos de lo que la cola DMA muere. Además, si el controlador administra un gran número de flujos directSound acelerados por hardware y cada secuencia genera sus propias interrupciones, el impacto acumulativo de todas las interrupciones puede degradar el rendimiento del sistema. En estas circunstancias, el controlador de minipuerto debe evitar el uso de interrupciones de hardware para programar el mantenimiento de secuencias individuales. En su lugar, debe atender todos los flujos en un único DPC programado para ejecutarse en intervalos generados por el temporizador normales.
Al establecer el intervalo de temporizador en 10 milisegundos, el intervalo entre ejecuciones DPC sucesivas es similar al descrito anteriormente para la interrupción del hardware en el caso de una única secuencia de reproducción de KMixer. Por lo tanto, el DPC puede controlar la secuencia de reproducción de KMixer además de las secuencias de DirectSound aceleradas por hardware.
Cuando la última secuencia sale del estado RUN, el controlador de minipuerto debe deshabilitar el DPC del temporizador para evitar perder ciclos de CPU del sistema. Inmediatamente después de deshabilitar el DPC, el controlador debe asegurarse de que se vacían los eventos de reloj o posición pendientes en las secuencias que se han ejecutado anteriormente. En Windows 98/Me y Windows 2000, el controlador debe llamar a Notify para desencadenar los eventos pendientes en las secuencias que se están pausando. En Windows XP y versiones posteriores, el sistema operativo desencadena automáticamente los eventos pendientes cuando una secuencia sale del estado RUN, sin necesidad de intervención del controlador de miniport.
Pausar/adquirir optimizaciones
En Windows 98/Me y Windows 2000, la rutina del servicio de transmisión del controlador de puerto WavePci (el método RequestService ) siempre genera una llamada al método IMiniportWavePciStream::Service del controlador de miniport, independientemente de si la secuencia está en estado RUN. En estos sistemas operativos, el método Service debe comprobar si la secuencia se está ejecutando antes de dedicar tiempo al trabajo real. (Sin embargo, si el DPC o ISR del controlador de miniporte ya se ha optimizado para llamar a Notify solo para las secuencias que se están ejecutando, agregar esta comprobación al método Service podría ser redundante).
En Windows XP y versiones posteriores, esta optimización no es necesaria porque el método Notify llama al método Service solo en secuencias que se ejecutan.
Usar la interfaz IPreFetchOffset
Los usuarios de DirectSound están familiarizados con los conceptos duales del cursor de reproducción y el cursor de escritura. El cursor de reproducción indica la posición en la secuencia de los datos que se emiten desde el dispositivo (la mejor estimación del controlador de la muestra actualmente en la DAC). La posición de escritura es la posición en la secuencia del siguiente lugar seguro para que el cliente escriba datos adicionales. Para WavePci, la suposición predeterminada es que el cursor de escritura se coloca al final de la última asignación solicitada. Si el controlador de minipuerto ha adquirido un gran número de asignaciones pendientes, el desplazamiento entre el cursor de reproducción y el cursor de escritura puede ser muy grande, lo suficientemente grande como para producir errores en determinadas pruebas de posición de audio WHQL. En Windows XP y versiones posteriores, la interfaz IPreFetchOffset soluciona estos problemas.
El controlador miniport usa IPreFetchOffset para especificar las características de captura previa del hardware de bus-master, que están determinadas en gran medida por el tamaño fiFO de hardware. El subsistema de audio usa estos datos para establecer un desplazamiento constante entre el cursor de reproducción y el cursor de escritura. Este desplazamiento constante, que puede ser significativamente menor que el desplazamiento predeterminado, aprovecha el hecho de que los datos se pueden escribir en una asignación incluso después de que la asignación se haya entregado al hardware, siempre y cuando el cursor de reproducción esté lo suficientemente lejos de la ubicación en la que se escriben los datos. (En esta instrucción se supone que el controlador no copia ni manipula los datos en las asignaciones). Un desplazamiento típico puede estar en el orden de 64 muestras, dependiendo del diseño del motor. Con un desplazamiento de este pequeño, un controlador WavePci puede ser totalmente dinámico y funcional mientras sigue solicitando un gran número de asignaciones.
Tenga en cuenta que DirectSound rellena actualmente el cursor de escritura de un pin acelerado por hardware en 10 milisegundos.
Para obtener más información, consulte Desplazamientos de captura previa.
Procesamiento de datos en asignaciones
Evite que el controlador de hardware toque los datos en las asignaciones si es posible. Cualquier procesamiento de software de los datos contenidos en las asignaciones debe dividirse en un filtro de software independiente del controlador de hardware. El hecho de que un controlador de hardware realice este procesamiento reduce su eficacia y crea problemas de latencia.
Un controlador de hardware debe esforzarse por ser transparente sobre sus capacidades de hardware reales. El controlador nunca debe reclamar que proporcione compatibilidad de hardware con una transformación de datos que se realiza realmente en el software.
Primitivos de sincronización
Es menos probable que un controlador tenga problemas de interbloqueo o rendimiento ahora y, en el futuro, si su código está diseñado para evitar que se bloquee siempre que sea posible. En concreto, el subproceso de ejecución de un controlador debe esforzarse por ejecutarse hasta completarse sin el riesgo de detener mientras espera otro subproceso o recurso. Por ejemplo, los subprocesos de controladores pueden usar las funcionesXxx interbloqueadas (por ejemplo, vea InterlockedIncrement) para coordinar sus accesos a determinados recursos compartidos sin el riesgo de bloquearse.
Aunque estas son técnicas eficaces, es posible que no pueda quitar de forma segura todos los bloqueos de número, las exclusión mutuas y otros primitivos de sincronización de bloqueo de la ruta de acceso de ejecución. Use las funcionesXxx interbloqueadas de manera sensata, con el conocimiento de que una espera indefinida podría provocar el hambre de los datos.
Por encima de todo, no cree primitivos de sincronización personalizados. Es probable que los primitivos de Windows integrados (exclusión mutua, bloqueos de número, etc.) se modifiquen según sea necesario para admitir nuevas características del programador en el futuro, y se garantiza que un controlador que usa construcciones personalizadas no funcione en el futuro.
IPinCount (interfaz)
En Windows XP y versiones posteriores, la interfaz IPinCount proporciona una manera de que un controlador de miniporte tenga en cuenta con mayor precisión los recursos de hardware que se consumen asignando un pin. Al llamar al método IPinCount::P inCount del controlador de miniport, el controlador de puerto hace lo siguiente:
Expone los recuentos de patillas actuales del filtro (según lo mantiene el controlador de puerto) al controlador de minipuerto.
Proporciona al controlador de minipuerto la oportunidad de revisar los recuentos de patillas para reflejar dinámicamente la disponibilidad actual de los recursos de hardware.
En algunos dispositivos de audio, las secuencias de onda con atributos diferentes (3D, estéreo/mono, etc.) también pueden tener diferentes "pesos" en términos de cuántos recursos de hardware consumen. Al abrir o cerrar una secuencia "ligera", el controlador incrementa o disminuye el recuento de patillas disponibles por uno. Sin embargo, al abrir una secuencia de "peso pesado", es posible que el controlador de miniporte necesite disminuir el número de patillas disponibles en dos en lugar de uno para indicar con mayor precisión el número de patillas que se pueden crear con los recursos restantes.
El proceso se invierte cuando se cierra una secuencia de peso pesado. El número de patillas disponibles puede aumentar en más de uno para reflejar el hecho de que se pueden crear dos o más secuencias ligeras a partir de los recursos recién liberados.