WavePci 미니포트 드라이버의 성능 문제
다음과 같은 일반적인 원칙에 따라 오디오 드라이버가 시스템에 미치는 성능 영향을 크게 줄일 수 있습니다.
일반 작업 중에 실행되는 코드를 최소화합니다.
필요한 경우에만 코드를 실행합니다.
총 시스템 리소스 사용량(CPU 로드뿐만 아니라)을 고려합니다.
속도 및 크기에 대한 코드를 최적화합니다.
또한 WavePci 미니포트 드라이버는 오디오 디바이스와 관련된 몇 가지 성능 문제를 해결해야 합니다. 다음 설명에서는 주로 오디오 렌더링 문제를 다루지만 제안된 기술 중 일부는 오디오 캡처에도 적용됩니다.
스트림 서비스 메커니즘
성능 최적화에 대해 논의하기 전에 스트림을 서비스하기 위한 WavePci 메커니즘을 이해하려면 일부 배경이 필요합니다.
웨이브 렌더링 또는 캡처 스트림을 처리할 때 오디오 디바이스는 미니포트 드라이버에서 정기적으로 서비스를 받아야 합니다. 스트림에 새 매핑을 사용할 수 있는 경우 드라이버는 해당 매핑을 스트림의 DMA 큐에 추가합니다. 또한 드라이버는 이미 처리된 모든 매핑을 큐에서 제거합니다. 매핑에 대한 자세한 내용은 WavePci 대기 시간을 참조하세요.
서비스를 수행하기 위해 미니포트 드라이버는 시스템 타이머 또는 DMA 기반 인터럽트에서 간격을 설정하는지 여부에 따라 DPC(지연 프로시저 호출) 또는 ISR(인터럽트 서비스 루틴)을 제공합니다. 후자의 경우 DMA 하드웨어는 일반적으로 일부 양의 스트림 데이터 전송을 완료하는 경우 매번 인터럽트를 트리거합니다.
DPC 또는 ISR이 실행 될 때마다 서비스가 필요한 스트림을 결정합니다. DPC 또는 ISR은 IPortWavePci::Notify 메서드를 호출하여 스트림을 서비스합니다. 이 메서드는 IServiceGroup 형식의 개체인 스트림의 서비스 그룹을 호출 매개 변수로 사용합니다. Notify 메서드는 서비스 그룹의 RequestService 메서드를 호출합니다(IServiceSink::RequestService 참조).
서비스 그룹 개체에는 IServiceSink 형식의 개체인 서비스 싱크 그룹이 포함됩니다. IServiceGroup 은 IServiceSink에서 파생되며 두 인터페이스 모두 RequestService 메서드가 있습니다. Notify 메서드가 서비스 그룹의 RequestService 메서드를 호출하면 서비스 그룹은 그룹의 각 서비스 싱크에서 RequestService 메서드를 호출하여 응답합니다.
스트림의 서비스 그룹에는 하나 이상의 서비스 싱크가 포함되어 있으며, 이 싱크는 스트림을 만든 직후 포트 드라이버가 서비스 그룹에 추가합니다. 포트 드라이버는 미니포트 드라이버의 IMiniportWavePci::NewStream 메서드를 호출하여 서비스 그룹에 대한 포인터를 가져옵니다. 서비스 싱크의 RequestService 메서드는 포트 드라이버의 스트림별 서비스 루틴입니다. 이 루틴은 다음을 수행합니다.
미니포트 드라이버의 IMiniportWavePciStream::Service 메서드를 호출합니다.
서비스 루틴이 마지막으로 실행된 이후 스트림에서 새로 보류 중인 위치 또는 클록 이벤트를 트리거합니다.
KS 이벤트에서 설명한 대로 클라이언트는 스트림이 특정 위치에 도달하거나 클록이 특정 타임스탬프를 도달할 때 알림을 받도록 등록할 수 있습니다. NewStream 메서드에는 서비스 그룹을 제공하지 않는 옵션이 있습니다. 이 경우 포트 드라이버는 서비스 루틴에 대한 호출 사이의 간격을 표시하도록 자체 타이머를 설정합니다.
NewStream 메서드와 마찬가지로 미니포트 드라이버의 IMiniportWavePci::Init 메서드도 서비스 그룹에 대한 포인터를 출력합니다. Init 호출 후 포트 드라이버는 서비스 싱크를 서비스 그룹에 추가합니다. 이 특정 서비스 싱크에는 필터 전체에 대한 서비스 루틴이 포함됩니다. 앞의 단락에서는 필터의 핀과 연결된 스트림에 대한 서비스 싱크를 설명합니다. 이 서비스 루틴은 미니포트 드라이버의 IMiniportWavePci::Service 메서드를 호출합니다. 서비스 루틴은 DPC 또는 ISR이 필터에 대한 서비스 그룹에 알림 을 호출할 때마다 실행됩니다. Init 메서드에는 서비스 그룹을 제공하지 않는 옵션이 있습니다. 이 경우 포트 드라이버는 필터 서비스 루틴을 호출하지 않습니다.
하드웨어 인터럽트
일부 미니포트 드라이버는 너무 많거나 충분하지 않은 하드웨어 인터럽트 중 하나를 생성합니다. DirectSound 하드웨어 가속을 사용하는 일부 WavePci 렌더링 디바이스에서 하드웨어 인터럽트는 매핑 공급이 거의 소진되고 렌더링 엔진이 고갈될 위험이 있는 경우에만 발생합니다. 다른 하드웨어 가속 WavePci 디바이스에서 하드웨어 인터럽트는 모든 단일 매핑 완료 또는 다른 비교적 작은 간격에서 발생합니다. 이 경우 ISR은 할 일이 거의 없다는 것을 자주 발견하지만 각 인터럽트는 여전히 스왑 등록 및 캐시 다시 로드가 있는 시스템 리소스를 사용합니다. 드라이버 성능을 개선하는 첫 번째 단계는 기아의 위험을 감수하지 않고 가능한 한 인터럽트 수를 줄이는 것입니다. 불필요한 인터럽트 제거 후 ISR을 보다 효율적으로 실행하도록 설계하여 추가 성능 향상을 달성할 수 있습니다.
일부 드라이버에서 ISR은 스트림이 실제로 실행 중인지 여부에 관계없이 하드웨어 인터럽트 발생 시 스트림의 Notify 메서드를 호출하여 시간을 낭비합니다. 실행 상태에 스트림이 없으면 DMA가 비활성 상태이며 모든 스트림에서 새 이벤트에 대한 매핑, 릴리스 매핑 또는 검사 획득하는 데 소요된 시간이 낭비됩니다. 효율적인 드라이버에서 ISR은 스트림의 Notify 메서드를 호출하기 전에 스트림이 실행 중인지 확인합니다.
그러나 이러한 유형의 ISR을 사용하는 드라이버는 스트림이 RUN 상태를 종료할 때 스트림의 보류 중인 이벤트가 트리거되는지 확인해야 합니다. 그렇지 않으면 이벤트가 지연되거나 손실될 수 있습니다. 이 문제는 Microsoft Windows XP보다 오래된 운영 체제에서 RUN-to-PAUSE 전환 중에만 발생합니다. Windows XP 이상에서 포트 드라이버는 스트림 상태가 RUN에서 PAUSE로 변경되면 즉시 모든 미해결 위치 이벤트를 자동으로 신호를 보냅니다. 그러나 이전 운영 체제에서 미니포트 드라이버는 스트림이 일시 중지된 직후 알림 에 대한 최종 호출을 하여 미해결 이벤트를 트리거할 책임이 있습니다. 자세한 내용은 아래의 PAUSE/ACQUIRE 최적화를 참조하세요.
일반적인 WavePci 미니포트 드라이버는 KMixer 시스템 드라이버에서 단일 재생 스트림을 관리합니다. KMixer의 현재 구현에서는 최소 3개의 매핑 IRP를 사용하여 재생 스트림을 버퍼링합니다. 각 IRP에는 약 10밀리초의 오디오에 충분한 버퍼 스토리지가 포함되어 있습니다. 미니포트 드라이버가 DMA 컨트롤러가 IRP에서 최종 매핑을 완료할 때마다 하드웨어 인터럽트를 트리거하는 경우 인터럽트는 상당히 규칙적인 10밀리초 간격으로 발생해야 하며, 이는 DMA 큐가 굶어 죽지 않도록 충분히 자주 발생합니다.
타이머 DPC
드라이버가 하드웨어 가속 DirectSound 스트림을 관리하는 경우 DMA 기반 하드웨어 인터럽트 대신 타이머 DPC( 타이머 개체 및 DPC 참조)를 사용해야 합니다. 마찬가지로, 온보드 타이머가 있는 PCI 카드 WavePci 디바이스는 DPC 대신 타이머 기반 하드웨어 인터럽트를 사용할 수 있습니다.
DirectSound 버퍼의 경우 전체 버퍼를 단일 IRP에 연결할 수 있습니다. 버퍼가 크고 미니포트 드라이버가 버퍼의 끝에 도달할 때만 하드웨어 인터럽트 일정을 예약하는 경우 연속 인터럽트는 지금까지 DMA 큐가 굶어 쳐서 발생할 수 있습니다. 또한 드라이버가 많은 수의 하드웨어 가속 DirectSound 스트림을 관리하고 각 스트림이 자체 인터럽트를 생성하는 경우 모든 인터럽트의 누적 영향으로 시스템 성능이 저하될 수 있습니다. 이러한 상황에서 미니포트 드라이버는 하드웨어 인터럽트 를 사용하여 개별 스트림의 서비스를 예약하지 않아야 합니다. 대신, 일반 타이머 생성 간격으로 실행되도록 예약된 단일 DPC의 모든 스트림을 서비스해야 합니다.
타이머 간격을 10밀리초로 설정하면 연속 DPC 실행 간의 간격은 단일 KMixer 재생 스트림의 경우 이전에 하드웨어 인터럽트에서 설명한 것과 유사합니다. 따라서 DPC는 하드웨어 가속 DirectSound 스트림 외에도 KMixer 재생 스트림을 처리할 수 있습니다.
마지막 스트림이 RUN 상태를 종료하면 미니포트 드라이버는 시스템 CPU 주기를 낭비하지 않도록 타이머 DPC를 사용하지 않도록 설정해야 합니다. DPC를 사용하지 않도록 설정하면 드라이버가 이전에 실행 중인 스트림에서 보류 중인 모든 클록 또는 위치 이벤트가 플러시되는지 확인해야 합니다. Windows 98/Me 및 Windows 2000에서 드라이버는 Notify 를 호출하여 일시 중지 중인 스트림에서 보류 중인 이벤트를 트리거해야 합니다. Windows XP 이상에서 운영 체제는 스트림이 실행 상태를 종료할 때 미니포트 드라이버의 개입 없이 보류 중인 이벤트를 자동으로 트리거합니다.
PAUSE/ACQUIRE 최적화
Windows 98/Me 및 Windows 2000에서 WavePci 포트 드라이버의 스트림 서비스 루틴( RequestService 메서드)은 스트림이 RUN 상태인지 여부에 관계없이 항상 미니포트 드라이버의 IMiniportWavePciStream::Service 메서드에 대한 호출을 생성합니다. 이러한 운영 체제에서 서비스 메서드는 실제 작업을 수행하기 전에 스트림이 실행 중인지 여부를 검사 합니다. 그러나 미니포트 드라이버의 DPC 또는 ISR이 실행 중인 스트림에 대해서만 Notify를 호출하도록 이미 최적화된 경우 이 검사 서비스 메서드에 추가하는 것은 중복될 수 있습니다.
Windows XP 이상에서는 Notify 메서드가 실행 중인 스트림에서만 Service 메서드를 호출하기 때문에 이 최적화가 필요하지 않습니다.
IPreFetchOffset 인터페이스 사용
DirectSound 사용자는 재생 커서 및 쓰기 커서의 이중 개념에 익숙합니다. 재생 커서는 디바이스에서 내보내는 데이터 스트림의 위치를 나타냅니다(현재 DAC에 있는 샘플에 대한 드라이버의 가장 좋은 추정치). 쓰기 위치는 클라이언트가 추가 데이터를 쓸 수 있는 다음 안전한 위치 스트림의 위치입니다. WavePci의 경우 기본 가정은 쓰기 커서가 요청된 마지막 매핑의 끝에 배치된다는 것입니다. 미니포트 드라이버가 많은 수의 미해결 매핑을 획득한 경우 재생 커서와 쓰기 커서 사이의 오프셋이 매우 클 수 있습니다. 특정 WHQL 오디오 위치 테스트에 실패할 만큼 충분히 큽니다. Windows XP 이상에서 IPreFetchOffset 인터페이스는 이러한 문제를 해결합니다.
미니포트 드라이버는 IPreFetchOffset을 사용하여 주로 하드웨어 FIFO 크기에 따라 결정되는 버스 master 하드웨어의 프리페치 특성을 지정합니다. 오디오 하위 시스템은 이 데이터를 사용하여 재생 커서와 쓰기 커서 간에 상수 오프셋을 설정합니다. 기본 오프셋보다 훨씬 작을 수 있는 이 상수 오프셋은 재생 커서가 데이터가 기록되는 위치에서 충분히 멀리 떨어져 있는 한 매핑이 하드웨어에 전달된 후에도 데이터를 매핑에 쓸 수 있다는 사실을 활용합니다. (이 문은 드라이버가 매핑에서 데이터를 복사하거나 조작하지 않는다고 가정합니다.) 일반적인 오프셋은 엔진 설계에 따라 64개의 샘플 순서에 있을 수 있습니다. 이 작은 오프셋을 사용하면 WavePci 드라이버는 많은 수의 매핑을 요청하면서 완전히 반응하고 작동할 수 있습니다.
DirectSound는 현재 하드웨어 가속 핀의 쓰기 커서를 10밀리초 단위로 채우고 있습니다.
자세한 내용은 오프셋 프리페치를 참조하세요.
매핑에서 데이터 처리
가능한 경우 하드웨어 드라이버가 매핑의 데이터를 터치하지 않도록 합니다. 매핑에 포함된 데이터의 모든 소프트웨어 처리는 하드웨어 드라이버와 별도로 소프트웨어 필터로 분할되어야 합니다. 하드웨어 드라이버가 이러한 처리를 수행하면 효율성이 저하되고 대기 시간 문제가 발생합니다.
하드웨어 드라이버는 실제 하드웨어 기능을 투명하게 하기 위해 노력해야 합니다. 드라이버는 소프트웨어에서 실제로 수행되는 데이터 변환에 대한 하드웨어 지원을 제공해야 한다고 주장해서는 안 됩니다.
동기화 기본 형식
가능하면 드라이버의 코드가 차단되지 않도록 설계된 경우 현재와 미래에 교착 상태 또는 성능 문제가 발생할 가능성이 적습니다. 특히 드라이버의 실행 스레드는 다른 스레드 또는 리소스를 기다리는 동안 중단될 위험 없이 완료될 때까지 실행하려고 노력해야 합니다. 예를 들어 드라이버 스레드는 InterlockedXxx 함수(예: InterlockedIncrement 참조)를 사용하여 차단될 위험 없이 특정 공유 리소스에 대한 액세스를 조정할 수 있습니다.
강력한 기술이지만 실행 경로에서 모든 스핀 잠금, 뮤텍스 및 기타 차단 동기화 기본 형식을 안전하게 제거하지 못할 수 있습니다. 무기한 대기로 인해 데이터가 굶주리게 될 수 있다는 지식과 함께 인터록된Xxx 함수를 신중하게 사용합니다.
무엇보다도 사용자 지정 동기화 기본 형식을 만들지 마세요. 기본 제공 Windows 기본 형식(뮤텍스, 스핀 잠금 등)은 나중에 새 스케줄러 기능을 지원하기 위해 필요에 따라 수정될 가능성이 높으며 사용자 지정 구문을 사용하는 드라이버는 나중에 작동하지 않도록 사실상 보장됩니다.
IPinCount 인터페이스
Windows XP 이상에서 IPinCount 인터페이스는 미니포트 드라이버가 핀을 할당하여 사용하는 하드웨어 리소스를 보다 정확하게 설명하는 방법을 제공합니다. 미니포트 드라이버의 IPinCount::P InCount 메서드를 호출하면 포트 드라이버는 다음을 수행합니다.
포트 드라이버에서 유지 관리하는 필터의 현재 핀 수를 미니포트 드라이버에 노출합니다.
미니포트 드라이버가 핀 수를 수정하여 하드웨어 리소스의 현재 가용성을 동적으로 반영할 수 있는 기회를 제공합니다.
일부 오디오 디바이스의 경우 서로 다른 특성(3차원, 스테레오/모노 등)이 있는 웨이브 스트림은 사용하는 하드웨어 리소스 수 측면에서 다른 "가중치"를 가질 수도 있습니다. "경량" 스트림을 열거나 닫을 때 드라이버는 사용 가능한 핀 수를 1씩 증가하거나 감소합니다. 그러나 "헤비급" 스트림을 열 때 미니포트 드라이버는 나머지 리소스로 만들 수 있는 핀 수를 보다 정확하게 나타내기 위해 사용 가능한 핀 수를 1개가 아닌 2개로 감소시켜야 할 수 있습니다.
이 프로세스는 헤비급 스트림이 닫혀 있을 때 반전됩니다. 새로 해제된 리소스에서 두 개 이상의 경량 스트림을 만들 수 있다는 사실을 반영하기 위해 사용 가능한 핀 수가 둘 이상 증가할 수 있습니다.