GNU/Linux >> Tutoriels Linux >  >> Linux

Détecter si le processeur a RDTSCP au moment de la compilation

GCC définit de nombreuses macros pour déterminer au moment de la compilation si une fonctionnalité particulière est prise en charge par la microarchitecture spécifiée à l'aide de -march . Vous pouvez trouver la liste complète dans le code source ici. Il est clair que GCC ne définit pas une telle macro pour RDTSCP (ou même RDTSC d'ailleurs). Les processeurs qui prennent en charge RDTSCP sont répertoriés dans :Quel est le type de processeur gcc qui inclut la prise en charge de RDTSCP ?.

Ainsi, vous pouvez créer votre propre liste (potentiellement incomplète) de microarchitectures prenant en charge RDTSCP . Ensuite, écrivez un script de construction qui vérifie l'argument passé à -march et voir s'il est dans la liste. Si c'est le cas, définissez une macro telle que __RDTSCP__ et utilisez-le dans votre code. Je suppose que même si votre liste est incomplète, cela ne devrait pas compromettre l'exactitude de votre code.

Malheureusement, les fiches techniques Intel ne semblent pas spécifier si un processeur particulier prend en charge RDTSCP même s'ils discutent d'autres fonctionnalités telles que AVX2.

Un problème potentiel ici est qu'il n'y a aucune garantie que chaque un processeur unique qui implémente une microarchitecture particulière, telle que Skylake, prend en charge RDTSCP . Je ne suis cependant pas au courant de telles exceptions.

En relation :Quel est le type de processeur gcc qui inclut la prise en charge de RDTSCP ?.

Pour déterminer la prise en charge de RDTSCP lors de l'exécution , le code suivant peut être utilisé sur des compilateurs prenant en charge les extensions GNU (GCC, clang, ICC), sur n'importe quel système d'exploitation x86. cpuid.h est livré avec le compilateur, pas le système d'exploitation.

#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() exécute CPUID deux fois :une fois pour vérifier le niveau maximum, une fois avec la valeur de feuille spécifiée. Il renvoie faux si le niveau demandé n'est même pas disponible, c'est pourquoi il fait partie d'un && expression. Vous ne voudrez probablement pas l'utiliser à chaque fois avant rdtscp, simplement comme initialiseur pour une variable, à moins qu'il ne s'agisse d'un simple programme ponctuel. Voir sur l'explorateur du compilateur Godbolt.

Pour MSVC, consultez Comment détecter la prise en charge de rdtscp dans Visual C++ ? pour le code utilisant son intrinsèque.

Pour certaines fonctionnalités du processeur connues de GCC, vous pouvez utiliser __builtin_cpu_supports pour vérifier un bitmap de fonctionnalité initialisé au début du démarrage.

// unfortunately no equivalent for RDTSCP
int sse42_supported() {
    return __builtin_cpu_supports("sse4.2");
}

Note de l'éditeur :https://gcc.gnu.org/wiki/DontUseInlineAsm . Cette réponse pendant longtemps n'était pas sûre, et plus tard éditée pour ne même pas compiler tout en étant toujours dangereuse (clobber RAX faisant le "a" contrainte insatisfaisante, tout en manquant toujours des clobbers sur les registres écrits par CPUID). Utilisez les intrinsèques dans une autre réponse. (Mais j'ai corrigé l'asm en ligne pour qu'il soit sûr et correct, au cas où quelqu'un le copierait/collerait, ou voudrait apprendre à utiliser correctement les contraintes et les clobbers.)

Après avoir enquêté un peu plus sur la base des suggestions faites par @Jason, j'ai maintenant une solution d'exécution (toujours pas de compilation) pour déterminer si RDTSCP existe en vérifiant le 28ème bit (voir bitmap de sortie) du cpuid instruction avec 0x80000001 comme entrée dans 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;
}

Si cela ne fonctionne pas dans le code PIC 32 bits à cause du clobber EBX, alors 1. arrêtez d'utiliser le PIC 32 bits car il est inefficace par rapport au PIC 64 bits ou par rapport à -fno-pie -no-pie exécutables. 2. obtenir un GCC plus récent qui autorise les clobbers EBX même en code PIC 32 bits, émettant des instructions supplémentaires pour sauvegarder/restaurer EBX ou tout ce qui est nécessaire. 3. utilisez la version intrinsèque (qui devrait résoudre ce problème pour vous).

Pour l'instant, je suis d'accord avec les compilateurs GNU, mais si quelqu'un a besoin de le faire sous MSVC, c'est un moyen intrinsèque de vérifier cela, comme expliqué ici.


Linux
  1. Quel processus a le Pid 0 ?

  2. Est-il possible de détecter quand un fichier a été téléchargé ?

  3. Comment détecter Bash>=4.0 ?

  4. Comment détecter le processeur graphique Intel ?

  5. États de processus Linux

Commande de temps Linux

Dernière commande sous Linux

Apricity OS a été abandonné !

Planification avec cron &At

L'indicateur de temps n'affiche pas la date/l'heure ?

Depuis combien de temps mon système Linux fonctionne-t-il ?