GNU/Linux >> Znalost Linux >  >> Linux

Získávání cyklů procesoru pomocí RDTSC – proč se hodnota RDTSC vždy zvyšuje?

Existuje mnoho matoucích a/nebo nesprávných informací o TSC, tak jsem si řekl, že se pokusím některé z nich objasnit.

Když Intel poprvé představil TSC (v původních CPU Pentium), bylo jasně zdokumentováno, že se počítají cykly (a ne čas). Tehdy však CPU většinou běžely s pevnou frekvencí, takže někteří lidé ignorovali zdokumentované chování a místo toho jej používali k měření času (zejména vývojáři linuxového jádra). Jejich kód se rozbil v pozdějších CPU, které neběží na pevné frekvenci (kvůli řízení spotřeby atd.). Přibližně v té době byli ostatní výrobci CPU (AMD, Cyrix, Transmeta atd.) zmateni a někteří implementovali TSC pro měření cyklů a někteří jej implementovali tak, aby měřilo čas, a někteří jej učinili konfigurovatelným (přes MSR).

Pak se "vícečipové" systémy staly běžnějšími pro servery; a ještě později byl představen vícejádrový. To vedlo k menším rozdílům mezi hodnotami TSC na různých jádrech (kvůli různým časům spouštění); ale co je důležitější, také to vedlo k velkým rozdílům mezi hodnotami TSC na různých CPU způsobených CPU běžícími různými rychlostmi (kvůli řízení spotřeby a/nebo jiným faktorům).

Lidé, kteří se to od začátku snažili používat špatně (lidé, kteří to používali k měření času a ne cyklů), si hodně stěžovali a nakonec přesvědčili výrobce CPU, aby standardizovali, že TSC měří čas a ne cykly.

Samozřejmě to byl průšvih - např. pokud podporujete všechna CPU 80x86, vyžaduje to hodně kódu, jen aby se zjistilo, co TSC skutečně měří; a různé technologie správy napájení (včetně věcí, jako je SpeedStep, ale také věcí, jako jsou stavy spánku) mohou ovlivnit TSC různými způsoby na různých CPU; takže AMD zavedlo příznak "TSC invariant" v CPUID, aby sdělilo OS, že TSC lze použít ke správnému měření času.

Všechny poslední procesory Intel a AMD jsou už nějakou dobu takové – TSC počítá čas a cykly vůbec neměří. To znamená, že pokud chcete měřit cykly, museli jste použít (pro konkrétní model) čítače sledování výkonu. Bohužel počítadla sledování výkonu jsou ještě horším nepořádkem (kvůli jejich specifické povaze modelu a spletité konfiguraci).


Dokud vaše vlákno zůstane na stejném jádru CPU, instrukce RDTSC se bude vracet stále větší číslo, dokud se neobejde. U 2GHz CPU se to stane po 292 letech, takže to není skutečný problém. Pravděpodobně se toho nedočkáte. Pokud očekáváte, že budete žít tak dlouho, ujistěte se, že se váš počítač restartuje, řekněme, každých 50 let.

Problém s RDTSC je v tom, že nemáte žádnou záruku, že se spustí ve stejný okamžik na všech jádrech staršího vícejádrového CPU a žádnou záruku, že se spustí ve stejný okamžik na všech CPU na starší desce s více CPU. .
Moderní systémy obvykle takové problémy nemají, ale problém lze také vyřešit na starších systémech nastavením afinity vlákna tak, aby běželo pouze na jednom CPU. To není dobré pro výkon aplikací, takže by se to obecně nemělo dělat, ale pro měření klíšťat je to v pořádku.

(Dalším „problémem“ je, že mnoho lidí používá RDTSC k měření času, což není co to dělá, ale psal jsi, že chceš cykly CPU, tak to je v pořádku. Pokud uděláte použijte k měření času RDTSC, můžete zažít překvapení, když se spustí úspora energie nebo hyperboost nebo jak se tomu říká množství technik změny frekvence. Pro skutečný čas je clock_gettime syscall je pod Linuxem překvapivě dobrý.)

Napsal bych jen rdtsc uvnitř asm příkaz, který pro mě funguje dobře a je čitelnější než nějaký obskurní hexadecimální kód. Za předpokladu, že se jedná o správný hexadecimální kód (a protože ani nepadne a nevrací stále se zvyšující číslo, zdá se, že ano), váš kód je dobrý.

Pokud chcete změřit počet zatržení, které kus kódu zabere, chcete rozdíl zaškrtnutí , stačí odečíst dvě hodnoty stále se zvyšujícího čítače. Něco jako uint64_t t0 = rdtsc(); ... uint64_t t1 = rdtsc() - t0;
Všimněte si, že pokud jsou nutná velmi přesná měření izolovaná od okolního kódu, musíte před voláním rdtsc serializovat, to znamená zastavit kanál. (nebo použijte rdtscp který je podporován pouze u novějších procesorů). Jediná instrukce serializace, kterou lze použít na každé úrovni oprávnění, je cpuid .

V odpovědi na další otázku v komentáři:

TSC začíná na nule, když zapnete počítač (a BIOS resetuje všechny čítače na všech CPU na stejnou hodnotu, i když některé BIOSy to před pár lety nedělaly spolehlivě).

Z pohledu vašeho programu tedy čítač spustil „nějaký neznámý čas v minulosti“ a vždy se zvyšuje s každým taktem, který CPU vidí. Pokud tedy provedete instrukci vracející tento čítač nyní a kdykoli později v jiném procesu, vrátí vyšší hodnotu (pokud mezi tím nebyl CPU pozastaven nebo vypnut). Různé běhy stejného programu získávají větší čísla, protože počítadlo neustále roste. Vždy.

Nyní clock_gettime(CLOCK_PROCESS_CPUTIME_ID) je jiná věc. Toto je čas procesoru, který OS dal procesu. Začíná na nule, když váš proces začíná. Nový proces také začíná na nule. Dva procesy běžící po sobě tak získají velmi podobná nebo stejná čísla, nikdy nerostoucí.

clock_gettime(CLOCK_MONOTONIC_RAW) je blíže tomu, jak RDTSC funguje (a na některých starších systémech je s ním implementován). Vrací hodnotu, která se stále zvyšuje. V dnešní době je to typicky HPET. To je však opravdu čas , a nikoli štítky . Pokud váš počítač přejde do stavu nízké spotřeby (např. běží na 1/2 normální frekvence), bude stále postupovat stejným tempem.


již dobré odpovědi a Damon to již svým způsobem zmínil ve své odpovědi, ale přidám to ze skutečného záznamu x86 manuálu (svazek 2, 4-301) pro RDTSC:

Načte aktuální hodnotu počítadla časové značky procesoru (64bitový MSR) do registrů EDX:EAX. Registr EDX je načten 32 bity vyššího řádu MSR a registr EAX je načten 32 bity nižšího řádu. (U procesorů, které podporují architekturu Intel 64, je vymazáno 32 bitů vyššího řádu každého z RAX a RDX.)

Procesor monotónně zvyšuje počítadlo časové značky MSR každý cyklus hodin a resetuje jej na 0 při každém resetování procesoru. Viz „Počítadlo časových razítek“ v kapitole 17 Příručky vývojáře softwaru Intel® 64 a IA-32 Architectures, svazek 3B , pro konkrétní podrobnosti o chování počítadla časové značky.


Linux
  1. Proč Windows 10 VM vždy ukazuje 100% využití CPU na QEMU-KVM?

  2. Proč potřebuje uživatel root oprávnění sudo?

  3. Proč stránka Apt-key Man nedoporučuje používat její příkaz Add?

  1. Proč se při používání Vlc spořič obrazovky neustále probouzí?

  2. Vrátí System.currentTimeMillis vždy hodnotu >=předchozí volání?

  3. Proč hlavní funkce bez příkazu return vrací hodnotu 12?

  1. Kdy jste naposledy použili Windows?

  2. kdevtmpfsi pomocí celého CPU

  3. Kolik uživatelů Linux podporuje přihlášení současně přes SSH?