2008-12-10 6 views
9

Le code suivant appelle les fonctions intégrées pour clz/ctz dans GCC et, sur d'autres systèmes, a des versions C. Évidemment, les versions C sont un peu sous-optimales si le système a une instruction clz/ctz intégrée, comme x86 et ARM.Comment utiliser les intrinsèques MSVC pour obtenir l'équivalent de ce code GCC?

#ifdef __GNUC__ 
#define clz(x) __builtin_clz(x) 
#define ctz(x) __builtin_ctz(x) 
#else 
static uint32_t ALWAYS_INLINE popcnt(uint32_t x) 
{ 
    x -= ((x >> 1) & 0x55555555); 
    x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); 
    x = (((x >> 4) + x) & 0x0f0f0f0f); 
    x += (x >> 8); 
    x += (x >> 16); 
    return x & 0x0000003f; 
} 
static uint32_t ALWAYS_INLINE clz(uint32_t x) 
{ 
    x |= (x >> 1); 
    x |= (x >> 2); 
    x |= (x >> 4); 
    x |= (x >> 8); 
    x |= (x >> 16); 
    return 32 - popcnt(x); 
} 
static uint32_t ALWAYS_INLINE ctz(uint32_t x) 
{ 
    return popcnt((x & -x) - 1); 
} 

#endif 

Quelles fonctions dois-je appeler, en-têtes qui dois-je inclure, etc pour ajouter un ifdef approprié pour MSVC ici? J'ai déjà regardé this page, mais je ne suis pas tout à fait sûr à quoi sert le #pragma (est-il nécessaire?) Et quelles restrictions il met sur les exigences de version MSVC pour la compilation. En tant que personne qui n'utilise pas vraiment MSVC, je ne sais pas non plus si ces intrinsèques ont des équivalents C sur d'autres architectures, ou si je dois #ifdef x86/x86_64 aussi bien quand # les définir.

+0

La page que vous faites référence ci-dessus fait référence à une fonction qui fait partie du moteur d'exécution .NET, vous essayez de construire votre programme pour .NET ou comme un exécutable Windows natif ? –

+0

C'est un exécutable Windows natif - une partie de la raison pour laquelle je demande est que j'ai trouvé plutôt difficile de trouver des pages de documentation Microsoft qui parlent réellement de C ces jours-ci. –

+0

Implémentation de Libcxx https://github.com/llvm-mirror/libcxx/blob/9dcbb46826fd4d29b1485f25e8986d36019a6dca/include/support/win32/support.h#L106-L182 – KindDragon

Répondre

1

Si MSVC a un compilateur intrinsèque pour cela, il sera ici:

Compiler Intrinsics on MSDN

Sinon, vous devrez écrire en utilisant __asm ​​

-2

Il y a deux intrinsics "_BitScanForward" et "_BitScanReverse", qui correspond au même but pour MSVC. Comprendre . Les fonctions sont les suivantes:

#ifdef _MSC_VER 
#include <intrin.h> 

static uint32_t __inline ctz(uint32_t x) 
{ 
    int r = 0; 
    _BitScanReverse(&r, x); 
    return r; 
} 

static uint32_t __inline clz(uint32_t x) 
{ 
    int r = 0; 
    _BitScanForward(&r, x); 
    return r; 
} 
#endif 

Il existe des versions 64 bits équivalentes "_BitScanForward64" et "_BitScanReverse64".

En savoir plus ici:

x86 Intrinsics on MSDN

+11

ctz & clz appellent les mauvaises fonctions (ils devraient utiliser _BitScanForward & BitScanReverse respectivement, pas BitScanReverse/BitScanForward) & clz est faux car il renvoie le décalage de l'ensemble de bits au lieu du nombre de zéros en tête. – Vitali

15

rebondissant à partir du code sh0dan, la mise en œuvre doit être corrigée comme ceci:

#ifdef _MSC_VER 
#include <intrin.h> 

uint32_t __inline ctz(uint32_t value) 
{ 
    DWORD trailing_zero = 0; 

    if (_BitScanForward(&trailing_zero, value)) 
    { 
     return trailing_zero; 
    } 
    else 
    { 
     // This is undefined, I better choose 32 than 0 
     return 32; 
    } 
} 

uint32_t __inline clz(uint32_t value) 
{ 
    DWORD leading_zero = 0; 

    if (_BitScanReverse(&leading_zero, value)) 
    { 
     return 31 - leading_zero; 
    } 
    else 
    { 
     // Same remarks as above 
     return 32; 
    } 
} 
#endif 

Comme commenté dans le code, les deux CTZ et clz ne sont pas définies si la valeur est 0. Dans notre abstraction, nous avons fixé __builtin_clz(value) comme (value?__builtin_clz(value):32) mais c'est un choix

+1

Un remplacement de '__builtin_clz()' dans MSVC presque 1-à-1 est '__lzcnt()'. Le matériel doit cependant prendre en charge SSE4. [Plus d'infos] (https://msdn.microsoft.com/fr-fr/library/bb384809.aspx). – rustyx

+1

Mon matériel prend en charge SSE4, mais pas BMI1, donc __lzcnt() se compile mais ne fait pas ce que j'attendais, fonctionnant plutôt comme un BSR. – GregC

+0

'31^__ builtin_clz' est égal à' _BitScanReverse' – KindDragon

-2

T ested sur linux et windows (x86):

#ifdef WIN32 
    #include <intrin.h> 
    static uint32_t __inline __builtin_clz(uint32_t x) { 
     unsigned long r = 0; 
     _BitScanReverse(&r, x); 
     return (31-r); 
    } 
#endif 

uint32_t clz64(const uint64_t x) 
{ 
    uint32_t u32 = (x >> 32); 
    uint32_t result = u32 ? __builtin_clz(u32) : 32; 
    if (result == 32) { 
     u32 = x & 0xFFFFFFFFUL; 
     result += (u32 ? __builtin_clz(u32) : 32); 
    } 
    return result; 
} 
+0

Avez-vous testé les performances de votre clz64? Je ne serais pas surpris que tout ce branchement le rende plus lent que la mise en œuvre de l'OP. – plamenko

Questions connexes