2008-12-10 8 views
9

El siguiente código llama a las funciones integradas para clz/ctz en GCC y, en otros sistemas, tiene versiones C. Obviamente, las versiones C son un poco subóptimas si el sistema tiene una instrucción clz/ctz incorporada, como x86 y ARM.¿Cómo usar los intrínsecos de MSVC para obtener el equivalente de este código 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 

¿Qué funciones tengo que llamar, que encabezados Qué necesito incluir, etc añadir un ifdef adecuada para MSVC aquí? Ya he consultado this page, pero no estoy del todo seguro para qué es #pragma (¿es necesario?) Y qué restricciones impone a los requisitos de la versión MSVC para la compilación. Como alguien que realmente no usa MSVC, tampoco sé si estos intrínsecos tienen C equivalentes en otras arquitecturas, o si también tengo que #ifdef x86/x86_64 cuando #definirlos.

+0

La página se hace referencia a anterior se refiere a una función que es parte del tiempo de ejecución de .NET, estás tratando de construir su programa para .NET o como un ejecutable nativo de Windows ? –

+0

Es un ejecutable nativo de Windows. Parte de la razón por la que estoy preguntando es que me ha resultado bastante difícil encontrar páginas de documentación de Microsoft que realmente hablen de C en estos días. –

+0

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

Respuesta

1

Si tiene un compilador MSVC intrínseca de este, que va a estar aquí:

Compiler Intrinsics on MSDN

De lo contrario, tendrá que escribir utilizando __asm ​​

-2

Hay dos características intrínsecas "_BitScanForward" y "_BitScanReverse", que se adapta al mismo propósito para MSVC. Incluir Las funciones son:

#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 

Hay versiones de 64 bits equivalentes "_BitScanForward64" y "_BitScanReverse64".

Leer más aquí:

x86 Intrinsics on MSDN

+11

ctz & clz llaman a las funciones incorrectas (deben estar usando _BitScanForward & BitScanReverse respectivamente, no BitScanReverse/BitScanForward) & clz es incorrecto ya que devuelve el desplazamiento del bit establecido en lugar del número de ceros iniciales. – Vitali

15

rebotando de código sh0dan, la aplicación debe ser corregida de esta manera:

#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 

como se ha comentado en el código, tanto ctz y clz son indefinido si el valor es 0. En nuestra abstracción, hemos fijado __builtin_clz(value) como (value?__builtin_clz(value):32) pero es una opción

+1

Un reemplazo casi 1 a 1 para '__builtin_clz()' en MSVC es '__lzcnt()'. Sin embargo, el hardware debe ser compatible con SSE4. [Más información] (https://msdn.microsoft.com/en-US/library/bb384809.aspx). – rustyx

+1

Mi hardware es compatible con SSE4, pero no con BMI1, por lo que __lzcnt() compila pero no hace lo que yo esperaría, sino que funciona como un BSR. – GregC

+0

'31^__ builtin_clz' es igual a' _BitScanReverse' – KindDragon

-2

T Ested en Linux y 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

¿Ha probado el rendimiento de su clz64? No me sorprendería que toda esta ramificación lo haga más lento que la implementación del PO. – plamenko

Cuestiones relacionadas