Condividi tramite


Temporizzazione dei giochi e processori multicore

Con le tecnologie di risparmio energia che diventano più comuni nei computer odierni, un metodo comunemente usato per ottenere intervalli cpu ad alta risoluzione, l'istruzione RDTSC, potrebbe non funzionare più come previsto. Questo articolo suggerisce una soluzione più accurata e affidabile per ottenere intervalli di CPU ad alta risoluzione usando le API di Windows QueryPerformanceCounter e QueryPerformanceFrequency.

Sfondo

Dall'introduzione del set di istruzioni X86 P5, molti sviluppatori di giochi hanno usato il contatore del timestamp di lettura, l'istruzione RDTSC, per eseguire tempi ad alta risoluzione. I timer multimediali di Windows sono abbastanza precisi per l'elaborazione audio e video, ma con tempi di fotogrammi di una dozzina di millisecondi o meno, non hanno una risoluzione sufficiente per fornire informazioni sul tempo differenziale. Molti giochi usano ancora un timer multimediale all'avvio per stabilire la frequenza della CPU e usano tale valore di frequenza per ridimensionare i risultati da RDTSC per ottenere un tempo accurato. A causa delle limitazioni di RDTSC, l'API Windows espone il modo più corretto per accedere a questa funzionalità tramite le routine di QueryPerformanceCounter e QueryPerformanceFrequency.

Questo uso di RDTSC per i tempi soffre di questi problemi fondamentali:

  • Valori discontinui. L'uso diretto di RDTSC presuppone che il thread sia sempre in esecuzione sullo stesso processore. I sistemi multiprocessore e dual core non garantiscono la sincronizzazione dei contatori dei cicli tra core. Questo è esacerbato quando combinato con le moderne tecnologie di risparmio energia che inattive e ripristinano vari core in momenti diversi, il che comporta in genere l'interruzione della sincronizzazione dei core. Per un'applicazione, in genere si verifica un errore o un potenziale arresto anomalo quando il thread passa tra i processori e ottiene valori di intervallo che generano delta di grandi dimensioni, delta negativi o intervallo interrotto.
  • Disponibilità di hardware dedicato. RDTSC blocca le informazioni di temporizzazione richieste dall'applicazione al contatore del ciclo del processore. Per molti anni questo è stato il modo migliore per ottenere informazioni di temporizzazione ad alta precisione, ma le schede madre più recenti sono ora inclusi dispositivi di temporizzazione dedicati che forniscono informazioni di temporizzazione ad alta risoluzione senza gli svantaggi di RDTSC.
  • Variabilità della frequenza della CPU. Si presuppone spesso che la frequenza della CPU sia fissa per la durata del programma. Tuttavia, con le moderne tecnologie di risparmio energia, si tratta di un presupposto non corretto. Sebbene inizialmente limitato ai computer portatili e ad altri dispositivi mobili, la tecnologia che modifica la frequenza della CPU è in uso in molti PC desktop di fascia alta; la disabilitazione della relativa funzione per mantenere una frequenza coerente non è in genere accettabile per gli utenti.

Consigli

I giochi necessitano di informazioni accurate sulla tempistica, ma è anche necessario implementare il codice di temporizzazione in modo da evitare i problemi associati all'uso di RDTSC. Quando si implementano tempi ad alta risoluzione, seguire questa procedura:

  1. Usare QueryPerformanceCounter e QueryPerformanceFrequency anziché RDTSC. Queste API possono usare RDTSC, ma potrebbero invece usare dispositivi di temporizzazione nella scheda madre o altri servizi di sistema che forniscono informazioni di temporizzazione ad alta risoluzione di alta qualità. Anche se RDTSC è molto più veloce di QueryPerformanceCounter, poiché quest'ultimo è una chiamata API, è un'API che può essere chiamata diverse centinaia di volte per fotogramma senza alcun impatto evidente. Tuttavia, gli sviluppatori devono tentare di chiamare QueryPerformanceCounter il minor numero possibile di giochi per evitare eventuali penalità per le prestazioni.

  2. Quando si calcolano i delta, i valori devono essere bloccati per garantire che i bug nei valori di intervallo non causino arresti anomali o calcoli correlati al tempo instabile. L'intervallo di blocca deve essere compreso tra 0 (per evitare valori differenziali negativi) e un valore ragionevole in base alla frequenza dei fotogrammi prevista più bassa. È probabile che il blocco sia utile in qualsiasi debug dell'applicazione, ma assicurarsi di tenere presente se si esegue l'analisi delle prestazioni o si esegue il gioco in modalità non ottimizzata.

  3. Calcolare tutti i tempi in un singolo thread. Il calcolo dei tempi in più thread, ad esempio con ogni thread associato a un processore specifico, riduce notevolmente le prestazioni dei sistemi multicore.

  4. Impostare tale thread singolo in modo che rimanga su un singolo processore usando l'API Windows SetThreadAffinityMask. In genere, questo è il thread principale del gioco. Anche se QueryPerformanceCounter e QueryPerformanceFrequency vengono in genere regolati per più processori, i bug nel BIOS o nei driver possono comportare la restituzione di valori diversi quando il thread passa da un processore a un altro. È quindi consigliabile mantenere il thread su un singolo processore.

    Tutti gli altri thread devono funzionare senza raccogliere i propri dati timer. Non è consigliabile usare un thread di lavoro per calcolare i tempi, perché questo diventerà un collo di bottiglia della sincronizzazione. I thread di lavoro devono invece leggere i timestamp dal thread principale e, poiché i thread di lavoro leggono solo timestamp di lettura, non è necessario usare sezioni critiche.

  5. Chiamare QueryPerformanceFrequency una sola volta, perché la frequenza non cambia durante l'esecuzione del sistema.

Compatibilità delle applicazioni

Molti sviluppatori hanno fatto ipotesi sul comportamento di RDTSC in molti anni, quindi è abbastanza probabile che alcune applicazioni esistenti visualizzino problemi quando vengono eseguiti in un sistema con più processori o core a causa della tempistica di implementazione. Questi problemi si manifestano in genere come movimento di glitch o lento movimento. Non esiste un rimedio semplice per le applicazioni che non sono consapevoli del risparmio energia, ma esiste uno shim esistente per forzare l'esecuzione di un'applicazione su un singolo processore in un sistema multiprocessore.

Per creare questo shim, scaricare Microsoft Application Compatibility Toolkit da Compatibilità delle applicazioni di Windows.

Usando l'amministratore della compatibilità, parte del toolkit, creare un database dell'applicazione e le correzioni associate. Creare una nuova modalità di compatibilità per questo database e selezionare la correzione di compatibilità SingleProcAffinity per forzare l'esecuzione di tutti i thread dell'applicazione in un singolo processore/core. Usando lo strumento da riga di comando Fixpack.exe (anche parte del toolkit), è possibile convertire questo database in un pacchetto installabile per l'installazione, il test e la distribuzione.

Per istruzioni sull'uso di Compatibility Administrator, vedere la documentazione del toolkit. Per la sintassi per ed esempi relativi all'uso di Fixpack.exe, vedere la Guida della riga di comando.

Per informazioni orientate ai clienti, vedere gli articoli knowledge base seguenti della Guida e del supporto tecnico Microsoft: