Domanda

Sto eseguendo il porting di un gioco, originariamente scritto per l'API Win32, su Linux (beh, sto eseguendo il porting del port OS X del port Win32 su Linux).

Ho implementato QueryPerformanceCounter fornendo gli uSecondi dall'avvio del processo:

BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount)
{
    gettimeofday(&currentTimeVal, NULL);
    performanceCount->QuadPart = (currentTimeVal.tv_sec - startTimeVal.tv_sec);
    performanceCount->QuadPart *= (1000 * 1000);
    performanceCount->QuadPart += (currentTimeVal.tv_usec - startTimeVal.tv_usec);

    return true;
}

Questo, insieme a QueryPerformanceFrequency() dando una costante 1.000.000 come frequenza, funziona bene sulla mia macchina, dandomi una variabile a 64 bit che contiene uSeconds dall'avvio del programma.

COSÌ è portatile? Non voglio scoprire che funziona diversamente se il kernel è stato compilato in un certo modo o qualcosa del genere.Tuttavia, mi va bene che non sia portabile su qualcosa di diverso da Linux.

È stato utile?

Soluzione

Forse.Ma hai problemi più grandi. gettimeofday() può causare tempi errati se sul sistema sono presenti processi che modificano il timer (ad esempio, ntpd).Su un Linux "normale", però, credo che la risoluzione di gettimeofday() è 10us.Può saltare avanti e indietro e, di conseguenza, nel tempo, in base ai processi in esecuzione sul sistema.Questo rende effettivamente la risposta alla tua domanda no.

Dovresti esaminare clock_gettime(CLOCK_MONOTONIC) per gli intervalli di temporizzazione.Soffre di molti meno problemi dovuti a cose come sistemi multi-core e impostazioni dell'orologio esterno.

Inoltre, esamina il clock_getres() funzione.

Altri suggerimenti

Alta risoluzione e tempi di sovraccarico ridotti per i processori Intel

Se utilizzi hardware Intel, ecco come leggere il contatore delle istruzioni in tempo reale della CPU.Ti dirà il numero di cicli della CPU eseguiti da quando il processore è stato avviato.Questo è probabilmente il contatore a grana più fine che puoi ottenere per la misurazione delle prestazioni.

Si noti che questo è il numero di cicli della CPU.Su Linux puoi ottenere la velocità della CPU da /proc/cpuinfo e dividerla per ottenere il numero di secondi.Convertirlo in un doppio è abbastanza utile.

Quando lo eseguo sulla mia scatola, ottengo

11867927879484732
11867927879692217
it took this long to call printf: 207485

Ecco il Guida per gli sviluppatori Intel che fornisce tonnellate di dettagli.

#include <stdio.h>
#include <stdint.h>

inline uint64_t rdtsc() {
    uint32_t lo, hi;
    __asm__ __volatile__ (
      "xorl %%eax, %%eax\n"
      "cpuid\n"
      "rdtsc\n"
      : "=a" (lo), "=d" (hi)
      :
      : "%ebx", "%ecx");
    return (uint64_t)hi << 32 | lo;
}

main()
{
    unsigned long long x;
    unsigned long long y;
    x = rdtsc();
    printf("%lld\n",x);
    y = rdtsc();
    printf("%lld\n",y);
    printf("it took this long to call printf: %lld\n",y-x);
}

@Bernardo:

Devo ammettere che la maggior parte del tuo esempio mi è andato oltre la testa.Si compila e sembra funzionare, però.È sicuro per i sistemi SMP o SpeedStep?

Questa è una bella domanda...Penso che il codice sia ok.Da un punto di vista pratico, lo usiamo ogni giorno nella mia azienda e corriamo su una vasta gamma di scatole, di tutto da 2-8 core.Naturalmente, YMMV, ecc., Ma sembra essere un metodo di tempo affidabile e basso (perché non fa passare un contesto allo spazio di sistema).

Generalmente il funzionamento è:

  • Dichiarare il blocco del codice come assemblatore (e volatile, quindi l'ottimizzatore lo lascerà da solo).
  • eseguire l'istruzione CPUID.Oltre a ottenere alcune informazioni sulla CPU (con cui non facciamo nulla) sincronizza il buffer di esecuzione della CPU in modo che i tempi non siano influenzati dall'esecuzione fuori ordinazione.
  • eseguire l'esecuzione rdtsc (lettura timestamp).Ciò recupera il numero di cicli di macchina eseguiti da quando il processore è stato ripristinato.Questo è un valore a 64 bit, quindi con le attuali velocità della CPU si avvolgerà ogni 194 anni circa.È interessante notare che, nel riferimento originale di Pentium, notano che si avvolge ogni 5800 anni circa.
  • L'ultima coppia di linee memorizza i valori dai registri nelle variabili HI e LO e lo mettono nel valore di ritorno a 64 bit.

Note specifiche:

  • L'esecuzione fuori ordinazione può causare risultati errati, quindi eseguiamo l'istruzione "CPUID" che oltre a fornire alcune informazioni sulla CPU sincronizza anche qualsiasi esecuzione di istruzioni fuori servizio.

  • La maggior parte degli OS sincronizza i contatori sui CPU quando iniziano, quindi la risposta è buona per un paio di nano-secondi.

  • Il commento in letargo è probabilmente vero, ma in pratica probabilmente non ti interessa i tempi attraverso i confini del letargo.

  • per quanto riguarda lo speedstep:Le CPU Intel più recenti compensano le variazioni di velocità e restituisce un conteggio adeguato.Ho fatto una rapida scansione su alcune delle scatole sulla nostra rete e ho trovato solo una scatola che non ce l'aveva:un Pentium 3 che esegue un vecchio server di database.(queste sono scatole Linux, quindi ho controllato con:grep costante_tsc /proc/cpuinfo)

  • Non sono sicuro delle CPU AMD, siamo principalmente un negozio Intel, anche se so che alcuni dei nostri guru dei sistemi di basso livello hanno fatto una valutazione AMD.

Spero che questo soddisfi la tua curiosità, è un'area di programmazione interessante e (IMHO) poco studiata.Sai quando Jeff e Joel stavano parlando se un programmatore dovesse sapere C?Stavo urlando contro di loro, "Ehi dimentica quella roba C di alto livello ...L'assemblatore è ciò che dovresti imparare se vuoi sapere cosa sta facendo il computer! "

Wine attualmente utilizza gettimeofday() per implementare QueryPerformanceCounter() ed è noto per far funzionare molti giochi Windows su Linux e Mac.

Inizia http://source.winehq.org/source/dlls/kernel32/cpu.c#L312

porta a http://source.winehq.org/source/dlls/ntdll/time.c#L448

Quindi dice esplicitamente i microsecondi, ma dice che la risoluzione dell'orologio di sistema non è specificata.Suppongo che la risoluzione in questo contesto significhi quanto verrà mai incrementato l'importo più piccolo?

La struttura dei dati è definita come avente microsecondi come unità di misura, ma ciò non significa che l'orologio o il sistema operativo siano effettivamente in grado di misurarli con precisione.

Come altre persone hanno suggerito, gettimeofday() è negativo perché l'impostazione dell'ora può causare uno spostamento dell'orologio e compromettere i calcoli. clock_gettime(CLOCK_MONOTONIC) è quello che vuoi, e clock_getres() ti dirà la precisione del tuo orologio.

La risoluzione effettiva di gettimeofday() dipende dall'architettura hardware.I processori Intel e le macchine SPARC offrono timer ad alta risoluzione che misurano i microsecondi.Altre architetture hardware utilizzano il timer del sistema, che in genere è impostato su 100 Hz.In questi casi, la risoluzione temporale sarà meno precisa.

Ho ottenuto questa risposta da Misurazione del tempo e timer ad alta risoluzione, Parte I

Questa risposta menziona problemi con la regolazione dell'orologio.Sia i tuoi problemi nel garantire le unità di tick sia i problemi con la regolazione dell'ora sono risolti in C++11 con il file <chrono> biblioteca.

L'orologio std::chrono::steady_clock è garantito che non verrà modificato e inoltre avanzerà a una velocità costante rispetto al tempo reale, quindi tecnologie come SpeedStep non devono influenzarlo.

Puoi ottenere unità typesafe convertendole in una delle std::chrono::duration specializzazioni, come ad es std::chrono::microseconds.Con questo tipo non c'è ambiguità riguardo alle unità utilizzate dal valore del tick.Tuttavia, tieni presente che l'orologio non ha necessariamente questa risoluzione.Puoi convertire una durata in attosecondi senza avere effettivamente un orologio così preciso.

Dalla mia esperienza e da quello che ho letto su Internet, la risposta è "No", non è garantito.Dipende dalla velocità della CPU, dal sistema operativo, dal tipo di Linux, ecc.

La lettura dell'RDTSC non è affidabile nei sistemi SMP, poiché ogni CPU mantiene il proprio contatore e non è garantita la sincronizzazione di ogni contatore rispetto ad un'altra CPU.

Potrei suggerire di provare clock_gettime(CLOCK_REALTIME).Il manuale posix indica che questo dovrebbe essere implementato su tutti i sistemi conformi.Può fornire un conteggio dei nanosecondi, ma probabilmente vorrai controllare clock_getres(CLOCK_REALTIME) sul tuo sistema per vedere qual è la risoluzione effettiva.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top