J'essaie de comparer les temps mesurés par c ++ 11 std :: chrono :: high_resolution_clock code> et le
rdtsc_clock code> horloge ci-dessous. À partir du
high_resolution_clock code>, je reçois un résultat de 11000, 3000, 1000, 0. Dans le
rdtsc_clock code>, je reçois 134, 15, 91, etc. Pourquoi leur résultat a-t-il l'air différemment? De mon intestin sentiment, je crois que le
rdtsc_clock code> présente les résultats précis, suis-je raison?
typedef std::chrono::high_resolution_clock Clock;
//typedef rdtsc_clock<3300000000> Clock;
typedef std::chrono::nanoseconds nanoseconds;
typedef std::chrono::duration<double, typename Clock::period> Cycle;
for(int n=0; n < 10; n++){
auto t1 = Clock::now();
//codes
auto t2 = Clock::now();
printf(%.0f ns \n", duration_cast<nanoseconds>(Cycle(t2 - t1)).count());
}
3 Réponses :
Si vous lisez des documents en ligne sur RDTSC, vous verrez qu'il ne garantira que des instructions d'après que l'instruction RDTSC ne soit pas exécutée dans le pipeline avant que l'instruction RDTSC elle-elle elle elle-même (ni que les instructions précédentes ne fonctionnent pas. après). Le conseil normal est d'utiliser une instruction CPUID immédiatement avant et / ou après la RDTSC pour déclencher de tels "points de séquence". De toute évidence, cela impacte les performances du programme et est plus souhaitable pour certaines types de mesures que d'autres (où les chiffres de débit moyen sont plus intéressants que les échantillons individuels). Vous pouvez vous attendre à ce que l'implémentation de la bibliothèque standard soit beaucoup plus prudente à ce sujet, ce qui peut être une raison pour laquelle ses mesures sont bien plus élevées. P>
Chaque noyau de la CPU conserve son propre registre TSC ... Si vous commencez simplement à prendre des échantillons sur un fil qui n'est pas lié à un noyau ou sur plusieurs threads non liés au même noyau, vous pouvez voir des sauts «étranges» dans des valeurs. . Certaines entreprises (par exemple, Microsoft) insistent sur le fait que l'abstraction matérielle Laye (HAL) est responsable de tenter d'obtenir les registres aussi proches que possible de la synchronisation possible, mais de nombreux PC (même de tout nouvel extrémité) ne manquent tout simplement pas de le faire. p>
Vous pouvez contourner cela en liant à un noyau ou en effectuant une étape d'étalonnage qui mesure les deltas croisés (avec une marge d'erreur d'étalonnage), puis ajustez les échantillons ultérieurs en fonction du noyau dont ils sont échantillonnés ( Ce qui lui-même est douloureux de déterminer sur la plupart des processeurs - vous devrez faire tourner des échantillons entre les instructions CPUID ou quelque chose de similaire). P>
le processus est explicitement «tâchesset» à un noyau et le code est un seul thread
@Bryanfok: D'autres problèmes ajoutés ci-dessus
Un autre problème avec RDTSC est qu'il mesure les cycles et les processeurs modernes ajuster dynamiquement leur horlogespeed afin de minimiser l'utilisation de la puissance et de contrôler la température de base qui dépend du processeur peut modifier la vitesse à laquelle la mise à jour du compteur ne contient aucune relation directe.
@MattNewport Je me trompe également sur l'ajustement que vous avez mentionné. On dirait que ma préoccupation est correcte. voir mon autre post. Stackoverflow.com/questions/ 19719317 / ...
@Tontyd remercie Tony pour avoir souligné l'instruction CPUID. Pour référence, je ne vois pas la différence (la moyenne calculée) après l'avoir ajoutée. Bien sûr, je courais un grand nombre de boucles.
@MattNewport: tout à fait à droite et très pertinent dans l'espace de périphérique portable
En fait, je suis capable de mesurer le temps a été doublé après avoir ajouté l'instruction CPUID.
Si cela ne tient toujours pas compte de l'ampleur des différences que vous observez, je suggère de jeter un coup d'œil au démontage pour les deux approches ... Vous pouvez trouver d'autres différences.
en.wikipedia.org/wiki/time_stamp_counter mentionne maintenant un rdtscp code > qui fait la même chose mais ne peut pas être réorganisé au niveau de la CPU, mais c'est nouveau et non dans de nombreux nouveaux processeurs encore
Je pense que vous ne comparez pas la même chose, sur mon Mac Cet exemple de travail, RDTSC et STD :: Chrono donne le même nombre de cycle, s'il peut aider:
#include <iostream> #include <vector> #include <numeric> #include <chrono> static __inline__ unsigned long long rdtsc(void){ unsigned hi, lo; __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 ); } static int sink = 0; int main(){ typedef std::ratio<1, 2400000000> period; // My mac @ 2.4 GHz unsigned long long int a,b; for (auto size = 1ull; size < 1000000000ull; size *= 100) { // record start time auto start = std::chrono::high_resolution_clock::now(); a = rdtsc(); // do some work std::vector<int> v(size, 42); sink = std::accumulate(v.begin(), v.end(), 0u); // make sure it's a side effect // record end time b = rdtsc(); auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration<double, period> diff = end-start; std::cout << "Time to fill and iterate a vector of " << size << " ints : " << diff.count() << " [cycle]" << ", old style: "<< b-a << " [cycle] \n"; } } Time to fill and iterate a vector of 1 ints : 13965.6 [cycle], old style: 13731 [cycle] Time to fill and iterate a vector of 100 ints : 1065.6 [cycle], old style: 969 [cycle] Time to fill and iterate a vector of 10000 ints : 68076 [cycle], old style: 67899 [cycle] Time to fill and iterate a vector of 1000000 ints : 5.4853e+06 [cycle], old style: 5483487 [cycle] Time to fill and iterate a vector of 100000000 ints : 6.57399e+08 [cycle], old style: 657395277 [cycle]
Je trouve des différences assez importantes à partir du code d'exemple de Timocafe sur mon ordinateur portable Mac P>
choses comme 0 fois est gênante. Cela indique que le compilateur a commandé des choses autour de la page High_Precision_Clock. Au moins avec notre code de montage RDTSC Tempories, nous pouvons utiliser volatile pour obtenir un comportement que nous désirons. Si je mettez les appels RDTSC à l'intérieur des appels High_Precision_Clock, je peux obtenir une horloge monotone, qui me dirait que c'est la volatilité de notre assemblée qui préserve la commande. De plus, le clang ++ 9.1.0 -O3
Il est temps de remplir et d'itérer un vecteur de 1 INTS: 27650.4 [CYCLE], style ancien: 35464 [CYCLE]
Il est temps de remplir et d'itérer un vecteur de 100 INTS: 763.2 [Cycle], style ancien: 939 [Cycle]
Il est temps de remplir et d'itérer un vecteur de 10000 INTS: 90712.8 [CYCLE], style ancien: 117181 [Cycle]
Il est temps de remplir et d'itérer un vecteur de 1000000 INTS: 4.79993E + 06 [cycle], ancien style: 6199891 [Cycle]
Il est temps de remplir et d'itérer un vecteur de 100000000 INTS: 4.80331E + 08 [cycle], style ancien: 620426953 [CYCLE] CODE> P>
g ++ 5.5 -O3
Il est temps de remplir et d'itérer un vecteur de 1 INTS: 2400 [cycle], style ancien: 1324 [Cycle]
Il est temps de remplir et d'itérer un vecteur de 100 INTS: 0 [cycle], ancien style: 944 [Cycle]
Il est temps de remplir et d'itérer un vecteur de 10000 INTS: 96000 [cycle], ancien style: 125444 [Cycle]
Il est temps de remplir et d'itérer un vecteur de 1000000 INTS: 5.4648E + 06 [cycle], ancien style: 7059362 [Cycle]
Il est temps de remplir et d'itérer un vecteur de 100000000 INTS: 5.05517E + 08 [cycle], ancien style: 652940006 [Cycle] Code> P>
time_point code> semble être dans différentes unités et exactitude sur les deux compilateurs. Ugh. P>
De plus, MSVC's
High_Resolution_Clock CODE> est mal implémenté à ce stade et a une résolution assez basse.