
Είμαι porting ένα παιχνίδι, που γράφτηκε αρχικά για το API του Win32, στο Linux (καλά, porting το λιμάνι OS X στο λιμάνι Win32 στο Linux).


Έχω υλοποιηθεί `QueryPerformanceCounter`δίνοντας τις uSeconds δεδομένου ότι η διαδικασία εκκίνησης: 



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

```

Αυτό, σε συνδυασμό με την `QueryPerformanceFrequency()`δίνοντας μια σταθερή 1000000 όπως η συχνότητα, λειτουργεί καλά **στον υπολογιστή μου** , που μου έδωσε μια μεταβλητή 64 bit, η οποία περιέχει `uSeconds`από το ξεκίνημα του προγράμματος.


Έτσι *είναι αυτό το φορητό;* Δεν θέλω να ανακαλύψετε λειτουργεί με διαφορετικό τρόπο αν ο πυρήνας συντάχθηκε με ένα συγκεκριμένο τρόπο ή κάτι τέτοιο. Είμαι μια χαρά με το που είναι μη φορητών με κάτι άλλο εκτός από Linux, όμως.

 
Είναι gettimeofday () εγγυημένη για να είναι το ψήφισμα του δευτερολέπτου;
==========================================================================



Μπορεί. Αλλά έχετε μεγαλύτερα προβλήματα. `gettimeofday()`μπορεί να οδηγήσει σε λανθασμένη χρονισμούς αν υπάρχουν διεργασίες στο σύστημά σας που αλλάζουν το χρονόμετρο (δηλαδή, ntpd). Σε ένα «κανονικό» linux, όμως, πιστεύω ότι η ανάλυση της `gettimeofday()`είναι 10us. Μπορεί να μεταβείτε προς τα εμπρός και προς τα πίσω και το χρόνο, κατά συνέπεια, με βάση τις διεργασίες που τρέχουν στο σύστημά σας. Το γεγονός αυτό καθιστά ουσιαστικά την απάντηση στην ερώτησή σας δεν είναι.


Θα πρέπει να εξετάσουμε `clock_gettime(CLOCK_MONOTONIC)`για το χρονοδιάγραμμα διαστήματα. Πάσχει από πολλά λιγότερα προβλήματα λόγω τα πράγματα όπως multi-core συστήματα και εξωτερικές ρυθμίσεις του ρολογιού.


Επίσης, εξετάζει την `clock_getres()`λειτουργία.

 

Από την εμπειρία μου, και από ό, τι έχω διαβάσει σε ολόκληρο το Διαδίκτυο, η απάντηση είναι «Όχι», αυτό δεν είναι εγγυημένο. Εξαρτάται από την ταχύτητα του επεξεργαστή, το λειτουργικό σύστημα, τη γεύση του Linux, κλπ

 


> 
> Η πραγματική ανάλυση της gettimeofday () εξαρτάται από την αρχιτεκτονική του υλικού. επεξεργαστές της Intel, καθώς και μηχανές SPARC προσφέρουν χρονόμετρα υψηλής ανάλυσης που μετρούν μικροδευτερόλεπτα. Άλλες αρχιτεκτονικές υλικού πέσει πίσω στο χρονόμετρο του συστήματος, το οποίο είναι τυπικά οριστεί σε 100 Ηζ. Σε τέτοιες περιπτώσεις, η ανάλυση χρόνου θα είναι λιγότερο ακριβείς.
> 
> 
> 


Έλαβα αυτή την απάντηση από [υψηλής ανάλυσης χρόνου Μέτρηση και χρονόμετρα, Μέρος Ι](http://web.archive.org/web/20160711223333/http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=272)

 

**Υψηλής Ανάλυσης, χαμηλή επιβάρυνση Χρονοδιάγραμμα για Intel Επεξεργαστές**


Αν είστε σε hardware της Intel, είναι εδώ πώς να διαβάσει τη CPU σε πραγματικό χρόνο μετρητής διδασκαλίας. Θα σας πω τον αριθμό των κύκλων CPU που εκτελέστηκαν αφού ο επεξεργαστής εκκίνηση. Αυτό είναι ίσως το καλύτερο κόκκους μετρητή μπορείτε να πάρετε για τη μέτρηση των επιδόσεων.


Σημειώστε ότι αυτό είναι ο αριθμός των κύκλων της CPU. Στο Linux μπορείτε να πάρετε την ταχύτητα της CPU από / proc / cpuinfo και διαιρούνται για να πάρει τον αριθμό των δευτερολέπτων. Η μετατροπή αυτή σε ένα διπλό είναι αρκετά βολικό.


Όταν τρέχω αυτό στο κουτί μου, παίρνω



```
11867927879484732
11867927879692217
it took this long to call printf: 207485

```

Εδώ είναι του [οδηγού προγραμματιστή Intel](http://cs.smu.ca/~jamuir/rdtscpm1.pdf) που δίνει τόνους λεπτομέρεια.



```
#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);
}

```
 


> 
> Γι 'αυτό λέει μικροδευτερόλεπτα ρητά, αλλά λέει ότι η επίλυση του ρολογιού συστήματος είναι απροσδιόριστη. Υποθέτω ανάλυση στο πλαίσιο αυτό σημαίνει πως θα μπορέσει ποτέ να αυξάνεται το μικρότερο ποσό αυτό;
> 
> 
> 


Η δομή δεδομένων ορίζεται ως έχον μικροδευτερόλεπτα ως μονάδα μέτρησης, αλλά αυτό δεν σημαίνει ότι το ρολόι ή το λειτουργικό σύστημα είναι στην πραγματικότητα ικανός να μετρά ότι λεπτώς.


Όπως και άλλοι έχουν προτείνει, `gettimeofday()`είναι κακό γιατί τον καθορισμό του χρόνου μπορεί να προκαλέσει το ρολόι παραποιήσει και να αποτινάξει τον υπολογισμό σας. `clock_gettime(CLOCK_MONOTONIC)`είναι ό, τι θέλετε, και `clock_getres()`θα σας πω την ακρίβεια του ρολογιού σας.

 

@Βερνάρδος:



> 
> Οφείλω να ομολογήσω, οι περισσότεροι από το παράδειγμά σας πήγε κατευθείαν πάνω από το κεφάλι μου. Κάνει την κατάρτιση, και φαίνεται να λειτουργεί, όμως. Είναι ασφαλές για τα συστήματα SMP ή SpeedStep;
> 
> 
> 


Αυτή είναι μια καλή ερώτηση ... Νομίζω ότι εντάξει τον κωδικό του. Από πρακτική άποψη, μπορούμε να το χρησιμοποιήσετε στην εταιρεία μου κάθε μέρα, και θα τρέξει σε ένα πολύ ευρύ φάσμα των κιβωτίων, τα πάντα 2-8 πυρήνες. Φυσικά, YMMV, κλπ, αλλά φαίνεται να είναι μια αξιόπιστη και χαμηλού γενικά (γιατί δεν κάνει μια αλλαγή πλαισίου στο σύστημα-χώρος) μέθοδο του χρόνου.


Σε γενικές γραμμές πώς λειτουργεί είναι:


* δηλώνουν το μπλοκ του κώδικα για να συναρμολόγησης (και πτητικά, έτσι ώστε η βελτιστοποίηση θα το αφήσετε μόνο του).
* εκτελέσει την εντολή CPUID. Εκτός από να πάρει κάποιες πληροφορίες CPU (που δεν κάνουμε τίποτα με) συγχρονίζει ρυθμιστικό εκτέλεσης της CPU, έτσι ώστε οι χρόνοι δεν επηρεάζονται από την εκτέλεση out-of-order.
* εκτελέσει την rdtsc (διαβάστε timestamp) εκτέλεση. Αυτό φέρνει τον αριθμό των κύκλων μηχανής που εκτελέστηκαν αφού ο επεξεργαστής μηδενίστηκε. Αυτή είναι μια τιμή 64-bit, τόσο με την τρέχουσα ταχύτητα της CPU θα τυλίξετε γύρω από κάθε 194 χρόνια περίπου. Είναι ενδιαφέρον ότι, στην αρχική αναφορά Pentium, σημειώνουν τυλίγει γύρω από κάθε 5800 χρόνια περίπου.
* τα τελευταία δύο γραμμές αποθηκεύει τις τιμές από τα μητρώα στις μεταβλητές hi και lo και βάλτε το στην τιμή επιστροφής 64-bit.


Ειδικές σημειώσεις:


* out-of-order εκτέλεση μπορεί να προκαλέσει εσφαλμένα αποτελέσματα, έτσι ώστε να εκτελέσει την «CPUID» εντολή η οποία εκτός από δίνοντάς σας κάποιες πληροφορίες για την CPU συγχρονίζει επίσης οποιαδήποτε out-of-order εκτέλεση της εντολής.
* Τα περισσότερα λειτουργικά συστήματα συγχρονίσετε τους μετρητές στο επεξεργαστές όταν αρχίζουν, οπότε η απάντηση είναι καλό να μέσα σε λίγα νανο-δευτερόλεπτα.
* Το σχόλιο χειμερία νάρκη είναι ίσως αλήθεια, αλλά στην πράξη ίσως δεν νοιάζονται για χρονισμούς πέρα ​​από τα όρια αδρανοποίησης.
* σχετικά με SpeedStep: Νεότερες Intel CPUs αντισταθμίσει τις αλλαγές ταχύτητας και επιστρέφει ένα προσαρμοσμένο καταμέτρηση. Έκανα μια γρήγορη σάρωση πάνω από μερικά από τα κουτιά στο δίκτυό μας και βρήκε μόνο ένα κουτί που δεν το έχουν: ένα Pentium 3 τρέχει κάποιο παλιό server της βάσης δεδομένων. (Αυτά είναι τα linux κουτιά, έτσι έλεγξα με: grep constant\_tsc / proc / cpuinfo)
* Δεν είμαι σίγουρος για την AMD επεξεργαστές, είμαστε κατά κύριο λόγο ένα κατάστημα της Intel, αν και ξέρω ότι κάποιοι από χαμηλού επιπέδου μας συστήματα γκουρού έκανε μια αξιολόγηση AMD.


Ελπίζω αυτό ικανοποιεί την περιέργειά σας, είναι μια ενδιαφέρουσα και (IMHO) σύμφωνα με μελετημένη περιοχή του προγραμματισμού. Ξέρετε, όταν ο Jeff και ο Joel μιλούσαν για το αν ή όχι ένας προγραμματιστής θα πρέπει να γνωρίζουν C; Ήμουν φωνάζει σε αυτούς, «hey ξεχνάμε αυτά τα πράγματα υψηλού επιπέδου C ... συναρμολόγησης είναι αυτό που θα πρέπει να μάθετε αν θέλετε να ξέρετε τι κάνει ο υπολογιστής!»

 

Το κρασί είναι στην πραγματικότητα χρησιμοποιώντας gettimeofday () για την εφαρμογή QueryPerformanceCounter () και είναι γνωστό ότι κάνει πολλά παιχνίδια των Windows δουλεύουν σε Linux και Mac.


ξεκινά <http://source.winehq.org/source/dlls/kernel32/cpu.c#L312>


οδηγεί σε <http://source.winehq.org/source/dlls/ntdll/time.c#L448>

 

Διαβάζοντας το RDTSC δεν είναι αξιόπιστη σε συστήματα SMP, αφού κάθε CPU διατηρεί το δικό τους μετρητή και κάθε μετρητής δεν είναι εγγυημένη για να με συγχρονισμένη σε σχέση με μια άλλη CPU.


Θα ήθελα να προτείνω την προσπάθεια **`clock_gettime(CLOCK_REALTIME)`**. Το εγχειρίδιο POSIX δείχνει ότι αυτό θα πρέπει να εφαρμοστεί σε όλα τα συμβατά συστήματα. Μπορεί να προσφέρει μια καταμέτρηση νανοδευτερόλεπτα, αλλά μάλλον θα θέλετε να ελέγξετε **`clock_getres(CLOCK_REALTIME)`**στο σύστημά σας για να δείτε ποια είναι η πραγματική ψήφισμα.

 

Μπορεί να σας ενδιαφέρει [Linux FAQ για`clock_gettime(CLOCK_REALTIME)`](http://juliusdavies.ca/posix_clocks/clock_realtime_linux_faq.html)

 

[Αυτή η απάντηση](https://stackoverflow.com/a/98/) αναφέρει προβλήματα με το ρολόι ρυθμίζεται. Τόσο τα προβλήματά σας εγγυάται μονάδες τσιμπούρι και τα προβλήματα με το χρόνο να προσαρμοστεί επιλύονται σε C ++ 11 με τη `<chrono>`βιβλιοθήκη.


Το ρολόι `std::chrono::steady_clock`δεν είναι εγγυημένη για να προσαρμοστεί, και επιπλέον θα προχωρήσει με σταθερό ρυθμό σε σχέση με το πραγματικό χρόνο, έτσι ώστε τεχνολογίες όπως SpeedStep δεν πρέπει να επηρεάσει.


Μπορείτε να πάρετε μονάδες typesafe με τη μετατροπή σε μία από τις `std::chrono::duration`ειδικότητες, όπως είναι `std::chrono::microseconds`. Με αυτόν τον τύπο δεν υπάρχει ασάφεια σχετικά με τις μονάδες που χρησιμοποιούνται από την τιμή τσιμπούρι. Ωστόσο, να έχετε κατά νου ότι το ρολόι δεν έχει κατ 'ανάγκη αυτό το ψήφισμα. Μπορείτε να μετατρέψετε μια διάρκεια σε attoseconds χωρίς πραγματικά ένα ρολόι που ακριβή.

 