Er gettimeofday () garantert å være av mikro oppløsning?

stemmer
85

Jeg porting et spill, som opprinnelig ble skrevet for Win32 API, til Linux (vel, porting OS X-porten på Win32 porten til Linux).

Jeg har implementert QueryPerformanceCounterved å gi uSeconds siden prosessen starter:

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;
}

Dette, kombinert med QueryPerformanceFrequency()noe som gir en konstant 1000000 som frekvens, fungerer bra på min maskin , gi meg en 64 bit variabel som inneholder uSecondssiden programmets oppstart.

er denne bærbare? Jeg ønsker ikke å oppdage det fungerer annerledes hvis kjernen ble utarbeidet på en bestemt måte, eller noe sånt. Jeg er fint med det å være ikke-bærbar til noe annet enn Linux, imidlertid.

Publisert på 01/08/2008 klokken 14:36
kilden bruker
På andre språk...                            


10 svar

stemmer
56

Kan være. Men du har større problemer. gettimeofday()kan resultere i feil timings hvis det er prosesser på systemet som endrer timer (dvs. ntpd). På en "normal" linux, men jeg tror oppløsningen på gettimeofday()er 10us. Det kan hoppe fremover og bakover og tid følgelig basert på prosesser som kjører på systemet ditt. Dette gjør effektivt svar på spørsmålet ditt no.

Du bør se nærmere på clock_gettime(CLOCK_MONOTONIC)for timing intervaller. Det lider flere mindre problemer på grunn av ting som flerkjernesystemer og eksterne klokkeinnstillingene.

Også se inn i clock_getres()funksjonen.

Svarte 01/08/2008 kl. 14:53
kilden bruker

stemmer
40

Høy oppløsning, lav overhead Timing for Intel-prosessorer

Hvis du er på Intel hardware, her er hvordan å lese CPU sanntid instruksjon teller. Den vil fortelle deg hvor mange CPU-sykluser utført siden prosessoren ble oppstartet. Dette er trolig den fineste kornet teller du kan få for resultatmåling.

Merk at dette er antall CPU-sykluser. På linux kan du få CPU hastigheten fra / proc / cpuinfo og dividere å få antall sekunder. Konvertering dette til en dobbel er ganske hendig.

Når jeg kjører dette på boksen min, får jeg

11867927879484732
11867927879692217
it took this long to call printf: 207485

Her er Intels utviklerveiledningen som gir tonnevis av detaljer.

#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);
}
Svarte 02/08/2008 kl. 08:08
kilden bruker

stemmer
18

@Bernard:

Jeg må innrømme, de fleste av ditt eksempel gikk rett over hodet mitt. Det gjør kompilere, og ser ut til å fungere, skjønt. Er dette trygt for SMP systemer eller Speedstep?

Det er et godt spørsmål ... Jeg tror koden er ok. Fra et praktisk ståsted, bruker vi det i mitt selskap hver dag, og vi kjører på et ganske bredt utvalg av bokser, alt fra 2-8 kjerner. Selvfølgelig, YMMV, etc, men det synes å være en pålitelig og lav-overhead (fordi det ikke gir en sammenheng bryter inn i system-space) Fremgangsmåte for timing.

Vanligvis hvordan det fungerer er:

  • erklære blokk med kode for å være assembler (og volatile, så Optimizer vil la det være).
  • utføre CPUID instruksjon. I tillegg til å få noen CPU informasjon (som vi ikke gjør noe med) den synkroniserer CPU henrettelse buffer slik at timings ikke påvirkes av ut-av-ordreutførelse.
  • utføre RDTSC-instruksjonen (lese tidsstempel) utførelse. Denne henter ut antallet maskinsykluser utført siden prosessoren ble nullstilt. Dette er en 64-bits verdi, så med dagens CPU hastigheter vil det brytes rundt hvert 194 år eller så. Interessant, i den opprinnelige Pentium referanse, merk de det brytes rundt hvert 5800 år eller så.
  • de siste par linjer lagre verdiene fra registerne til de variable hi og lo, og sette det inn i den 64-bits returverdi.

Spesifikke notater:

  • out-of-ordreutførelse kan forårsake feil resultater, så vi utføre "CPUID" instruksjon som i tillegg til å gi deg litt informasjon om cpu synkroniserer også noen out-of-order instruksjon utførelse.

  • De fleste OS er synkron tellerne på CPUer når de starter, så svaret er god til innen et par nanosekunder.

  • Den dvale kommentar er nok sant, men i praksis har du sannsynligvis ikke bryr seg om timings på tvers av dvale grenser.

  • om Speedstep: Nyere Intel CPUer kompensere for hastighetsendringer og returnerer en justert teller. Jeg gjorde en rask skanning over noen av boksene på nettverket vårt og fant bare en boks som ikke har det: en Pentium 3 kjører noen gamle databasetjeneren. (Disse er linux bokser, så jeg sjekket med: grep constant_tsc / proc / cpuinfo)

  • Jeg er ikke sikker på om AMD CPUer, vi er først og fremst en Intel butikk, selv om jeg vet at noen av våre lavnivå systemer guruer gjorde en AMD evaluering.

Håper dette tilfredsstiller din nysgjerrighet, det er en interessant og (IMHO) under studert området i programmering. Du vet når Jeff og Joel snakket om hvorvidt en programmerer bør vite C? Jeg ropte på dem, "hei glemme at høyt nivå C ting ... assembler er hva du bør vite hvis du ønsker å vite hva maskinen gjør!"

Svarte 04/08/2008 kl. 00:51
kilden bruker

stemmer
14

Du kan være interessert i Linux Vanlige spørsmål omclock_gettime(CLOCK_REALTIME)

Svarte 18/08/2008 kl. 15:51
kilden bruker

stemmer
11

Vin er faktisk bruker gettimeofday () for å implementere QueryPerformanceCounter () og det er kjent for å lage mange Windows-spill fungerer på Linux og Mac.

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

fører til http://source.winehq.org/source/dlls/ntdll/time.c#L448

Svarte 04/08/2008 kl. 14:44
kilden bruker

stemmer
10

Så det sier mikro eksplisitt, men sier oppløsningen av systemklokken er uspesifisert. Jeg antar at vedtak i denne sammenheng betyr hvor den minste mengden det noensinne vil bli økes?

Datastrukturen er definert som å ha mikrosekunder som en måleenhet, men det betyr ikke at klokken eller operativsystemet er faktisk i stand til å måle det fint.

Som andre har foreslått, gettimeofday()er dårlig fordi du setter den tid kan føre til klokken skew og kaste av beregningen. clock_gettime(CLOCK_MONOTONIC)er det du ønsker, og clock_getres()vil fortelle deg presisjonen din klokke.

Svarte 02/08/2008 kl. 17:57
kilden bruker

stemmer
9

Selve oppløsning av gettimeofday () avhenger av maskinvarearkitektur. Intel-prosessorer samt SPARC maskiner tilbyr høy oppløsning timere som måler mikrosekunder. Andre maskinvarearkitekturer falle tilbake til systemets tidsur, som typisk er satt til 100 Hz. I slike tilfeller vil tidsoppløsningen være mindre nøyaktige.

Jeg fikk dette svaret fra High Resolution Tid Måling og Timers, del I

Svarte 01/08/2008 kl. 14:55
kilden bruker

stemmer
5

Dette svaret nevner problemer med klokken justeres. Begge dine problemer garanterer tick enheter og problemene med tiden justeres løses i C ++ 11 med <chrono>biblioteket.

Klokken std::chrono::steady_clocker garantert ikke å bli justert, og dessuten det vil gå videre med en konstant hastighet i forhold til sann tid, så teknologier som Speedstep må ikke påvirke det.

Du kan få typesafe enheter ved å konvertere til en av std::chrono::durationspesialiseringer, for eksempel std::chrono::microseconds. Med denne typen er det ingen tvetydighet om enhetene som brukes av flåtten verdi. Men husk at klokken ikke nødvendigvis har denne oppløsningen. Du kan konvertere en varighet til attosekund uten egentlig å ha en klokke som nøyaktig.

Svarte 26/06/2012 kl. 15:57
kilden bruker

stemmer
5

Fra min erfaring, og fra hva jeg har lest over Internett, er svaret "Nei," det er ikke garantert. Det avhenger av CPU-hastighet, operativsystem, smaken av Linux, etc.

Svarte 01/08/2008 kl. 14:46
kilden bruker

stemmer
3

Lese RDTSC-instruksjonen er ikke pålitelig i SMP-systemer, siden hver CPU opprettholder sin egen disk og hver teller er ikke garantert å by synkronisert med hensyn til en annen CPU.

Jeg kan foreslå å prøve clock_gettime(CLOCK_REALTIME). POSIX manualen indikerer at dette skal gjennomføres på alle kompatible systemer. Det kan gi et nanosekund teller, men du sannsynligvis vil ønske å sjekke clock_getres(CLOCK_REALTIME)på systemet for å se hva den faktiske oppløsningen er.

Svarte 18/08/2008 kl. 15:40
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more