2008-12-10 3 views
9

Der folgende Code ruft die integrierten Funktionen für clz/ctz in GCC auf und hat auf anderen Systemen C-Versionen. Offensichtlich sind die C-Versionen ein bisschen suboptimal, wenn das System eine eingebaute clz/ctz-Anweisung hat, wie x86 und ARM.Wie benutzt man MSVC-Intrinsics, um das Äquivalent dieses GCC-Codes zu erhalten?

#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 

Welche Funktionen muss ich anrufen, die Header muss ich schließen, etc hier eine richtige ifdef für MSVC hinzufügen? Ich habe mir schon this page angesehen, aber ich bin mir nicht ganz sicher, wofür das #pragma ist (ist es erforderlich?) Und welche Einschränkungen es den MSVC-Versionsanforderungen für die Kompilierung auferlegt. Als jemand, der MSVC nicht wirklich benutzt, weiß ich auch nicht, ob diese intrinsischen Elemente C-Äquivalente auf anderen Architekturen haben, oder ob ich #ifdef x86/x86_64 auch verwenden muss, wenn ich # definiere.

+0

Die Seite, die Sie oben verweisen bezieht sich auf eine Funktion, die Teil der .NET-Laufzeit ist, versuchen Sie Ihr Programm für .NET oder als native ausführbare Windows-Datei zu erstellen ? –

+0

Es ist eine native Windows-ausführbare Datei - ein Grund dafür ist, dass ich es ziemlich schwierig finde, Microsoft-Dokumentationsseiten zu finden, die heutzutage tatsächlich über C sprechen. –

+0

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

Antwort

1

Wenn MSVC einen Compiler Eigen dafür hat, wird es hier sein:

Compiler Intrinsics on MSDN

Andernfalls werden Sie es schreiben __asm ​​mit

-2

Es gibt zwei Spezifika "_BitScanForward" und "_BitScanReverse", was dem gleichen Zweck für MSVC entspricht. Einbeziehen. Die Funktionen sind:

#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 

gleichwertige 64-Bit-Versionen "_BitScanForward64" und "_BitScanReverse64" Es gibt.

Lesen Sie hier mehr:

x86 Intrinsics on MSDN

+11

ctz & clz rufen die falschen Funktionen auf (sie sollten _BitScanForward & BitScanReverse verwenden BitScanReverse/BitScanForward) & clz ist falsch, da es den Offset des Bitsatzes anstelle der Anzahl der führenden Nullen zurückgibt. – Vitali

15

von sh0dan Code Bouncing sollte die Umsetzung wie folgt korrigiert werden:

#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 

Wie im Code kommentiert, sowohl ctz und clz nicht definiert sind, wenn Wert ist 0. In unserer Abstraktion haben wir __builtin_clz(value) als (value?__builtin_clz(value):32) behoben aber es ist eine Wahl

+1

Ein fast 1: 1 Ersatz für '__builtin_clz()' in MSVC ist '__lzcnt()'. Die Hardware muss jedoch SSE4 unterstützen. [Weitere Informationen] (https://msdn.microsoft.com/en-US/library/bb384809.aspx). – rustyx

+1

Meine Hardware unterstützt SSE4, aber nicht BMI1, also __lzcnt() kompiliert, tut aber nicht das, was ich erwarte, sondern arbeitet als BSR. – GregC

+0

'31^__ builtin_clz' ist gleich' _BitScanReverse' – KindDragon

-2

T sierte auf Linux und 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

Haben Sie die Leistung Ihrer clz64 getestet? Ich wäre nicht überrascht, dass all diese Verzweigungen es langsamer machen als die Implementierung des OP. – plamenko

Verwandte Themen