GCC definuje mnoho maker, která v době kompilace určí, zda je konkrétní funkce podporována mikroarchitekturou zadanou pomocí -march
. Celý seznam najdete ve zdrojovém kódu zde. Je jasné, že GCC takové makro pro RDTSCP
nedefinuje (nebo dokonce RDTSC
na to přijde). Procesory, které podporují RDTSCP
jsou uvedeny v:Jaký je typ gcc cpu, který zahrnuje podporu pro RDTSCP?.
Takže si můžete vytvořit svůj vlastní (potenciálně neúplný) seznam mikroarchitektur, které podporují RDTSCP
. Poté napište sestavení skriptu, který zkontroluje argument předaný do -march
a zjistěte, zda je v seznamu. Pokud ano, definujte makro, například __RDTSCP__
a použijte jej ve svém kódu. Předpokládám, že i když je váš seznam neúplný, nemělo by to ohrozit správnost vašeho kódu.
Bohužel se zdá, že datasheety Intelu neuvádějí, zda konkrétní procesor podporuje RDTSCP
i když diskutují o dalších funkcích, jako je AVX2.
Jedním z potenciálních problémů je, že neexistuje žádná záruka, že každý jeden procesor, který implementuje konkrétní mikroarchitekturu, jako je Skylake, podporuje RDTSCP
. Nejsem si však vědom takových výjimek.
Související:Jaký je typ cpu gcc, který zahrnuje podporu pro RDTSCP?.
Zjištění podpory RDTSCP za běhu , lze následující kód použít na kompilátorech podporujících rozšíření GNU (GCC, clang, ICC), na jakémkoli x86 OS. cpuid.h
je dodáván s kompilátorem, nikoli s OS.
#include <cpuid.h>
int rdtscp_supported(void) {
unsigned a, b, c, d;
if (__get_cpuid(0x80000001, &a, &b, &c, &d) && (d & (1<<27)))
{
// RDTSCP is supported.
return 1;
}
else
{
// RDTSCP is not supported.
return 0;
}
}
__get_cpuid()
spustí CPUID dvakrát:jednou pro kontrolu maximální úrovně, jednou se zadanou hodnotou listu. Vrací false, pokud požadovaná úroveň ani není k dispozici, proto je součástí &&
výraz. Pravděpodobně to nebudete chtít používat pokaždé před rdtscp, jen jako inicializátor pro proměnnou, pokud to není jen jednoduchý jednorázový program. Podívejte se na to v průzkumníku kompilátoru Godbolt.
Pro MSVC viz Jak zjistit podporu rdtscp ve Visual C++? pro kód využívající jeho vnitřní.
Pro některé funkce CPU, o kterých GCC ví, můžete použít __builtin_cpu_supports
pro kontrolu bitmapy funkce, která je inicializována na začátku spouštění.
// unfortunately no equivalent for RDTSCP
int sse42_supported() {
return __builtin_cpu_supports("sse4.2");
}
Poznámka editora:https://gcc.gnu.org/wiki/DontUseInlineAsm . Tato odpověď po dlouhou dobu nebyla bezpečná a později byla upravena tak, aby se nedala ani zkompilovat, a přitom byla stále nebezpečná (obtěžování RAX a vytváření "a"
omezení nesplnitelné, a přitom stále chybí clobbery v registrech, které CPUID zapisuje). Použijte vnitřní podstatu v jiné odpovědi. (Ale opravil jsem vložený asm v tomto, aby byl bezpečný a správný, pro případ, že by to někdo zkopíroval/vložil nebo se chtěl naučit, jak správně používat omezení a clobbery.)
Poté, co jsem trochu více prozkoumal na základě návrhů od @Jasona, mám nyní řešení za běhu (stále ne řešení v době kompilace), abych zjistil, zda RDTSCP
existuje kontrolou 28. bitu (viz výstupní bitmapa) z cpuid
instrukce s 0x80000001
jako vstup v EAX
.
int if_rdtscp() {
unsigned int edx;
unsigned int eax = 0x80000001;
#ifdef __GNUC__ // GNU extended asm supported
__asm__ ( // doesn't need to be volatile: same EAX input -> same outputs
"CPUID\n\t"
: "+a" (eax), // CPUID writes EAX, but we can't declare a clobber on an input-only operand.
"=d" (edx)
: // no read-only inputs
: "ecx", "ebx"); // CPUID writes E[ABCD]X, declare clobbers
// a clobber on ECX covers the whole RCX, so this code is safe in 64-bit mode but is portable to either.
#else // Non-gcc/g++ compilers.
// To-do when needed
#endif
return (edx >> 27) & 0x1;
}
Pokud to nefunguje v 32bitovém kódu PIC kvůli clobberu EBX, pak 1. přestaňte používat 32bitový PIC, protože je neefektivní v porovnání s 64bitovým PIC nebo vs. -fno-pie -no-pie
spustitelné soubory. 2. získejte novější GCC, který umožňuje clobbery EBX dokonce i v 32bitovém kódu PIC, vydávající další pokyny k uložení/obnovení EBX nebo čehokoli, co je potřeba. 3. použijte verzi intrinsics (která by vám měla toto obejít).
Zatím mi jsou s kompilátory GNU v pořádku, ale pokud to někdo potřebuje udělat pod MSVC, pak je vnitřní způsob, jak to zkontrolovat, jak je vysvětleno zde.