¿Cómo usar los intrínsecos de MSVC para obtener el equivalente de este código GCC?

6 minutos de lectura

avatar de usuario
Shikari oscuro

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.

#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 debo llamar, qué encabezados debo incluir, etc. para agregar un ifdef adecuado para MSVC aquí? ya he mirado esta página, pero no estoy del todo seguro de para qué sirve #pragma (¿es necesario?) y qué restricciones impone a los requisitos de la versión de MSVC para la compilación. Como alguien que realmente no usa MSVC, tampoco sé si estos intrínsecos tienen equivalentes en C en otras arquitecturas, o si tengo que #ifdef x86/x86_64 también al #definirlos.

  • La página a la que se refiere arriba se refiere a una función que es parte del tiempo de ejecución de .NET, ¿está tratando de construir su programa para .NET o como un ejecutable nativo de Windows?

    – Timo Geusch

    10 de diciembre de 2008 a las 13:49

  • Es un ejecutable nativo de Windows; parte de la razón por la que pregunto es que me ha resultado bastante difícil encontrar páginas de documentación de Microsoft que realmente hablen sobre C en estos días.

    – Shikari oscuro

    10 de diciembre de 2008 a las 18:04

  • Implementación de libcxx github.com/llvm-mirror/libcxx/blob/…

    – Dragón amable

    03/04/2017 a las 10:35

Rebotando desde el código sh0dan, la implementación debe corregirse así:

#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 comenta en el código, tanto ctz como clz no están definidos si el valor es 0. En nuestra abstracción, arreglamos __builtin_clz(value) como (value?__builtin_clz(value):32) pero es una elección

  • 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.

    – oxidado

    27 de enero de 2016 a las 10:23


  • 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

    14/03/2017 a las 15:36

  • 31 ^__builtin_clz es igual a _BitScanReverse

    – Dragón amable

    16 de febrero de 2018 a las 13:16

  • Tenga en cuenta que GNU C __builtin_ctz y clz también tienen un comportamiento indefinido cuando el valor de entrada es 0 (gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html); esto les permite en línea como un solo bsf instrucción (o 31 ^ bsr que funciona para el rango de salidas definidas). Si necesita manejar entradas posiblemente cero, entonces querrá envoltorios similares para las funciones integradas GNU C, por lo que lo apropiado sería tener una capa de portabilidad alrededor de BSF / 31 ^ BSR, y luego manejo cero encima de eso. .. Y usando lzcnt #ifdef __BMI1__.

    – Peter Cordes

    17 oct 2020 a las 10:04

  • Relacionado: VS: comportamiento de optimización inesperado con _BitScanReverse64 intrínseco: el MSVC no expone el comportamiento de destino no modificado de la instrucción asm, aunque tiene una API que pudo Haz eso. (Así que no necesita inicializar el index argumento de salida; sin embargo, no duele, el compilador sabe que es un operando de solo salida del intrínseco).

    – Peter Cordes

    17 oct 2020 a las 10:08

avatar de usuario
Ana Bets

Si MSVC tiene un compilador intrínseco para esto, estará aquí:

Intrínsecos del compilador en MSDN

De lo contrario, tendrás que escribirlo usando __asm

avatar de usuario
Jebearsica

Lo encontré en un sitio web coreano. https://torbjorn.tistory.com/317
En el compilador msvc, puede usar __lzcnt(unsigned int) para reemplazar __builtin_clz(unsigned int) en el compilador gcc.

  • Tenga en cuenta que el lzcnt instrucción requiere BMI1. En CPU más antiguas, se ejecuta como bsrdonación 31-lzcnt (y dejando el registro de destino sin modificar para input=0). GCC solo se expandirá __builtin_clz como lznct si compilas con -march=haswell u opciones similares.

    – Peter Cordes

    17 oct 2020 a las 9:56

  1. La función equivalente para int __builtin_ctz (int x sin signo) en MSVC es sin firmar int _tzcnt_u32 (sin firmar int a) por 32 bits entero y devuelve el recuento de ceros finales. Para 64 bits utilizar sin firmar __int64 _tzcnt_u64 (sin firmar __int64 a) 1.

  2. La función equivalente para int __builtin_clz (int x sin firmar) en MSVC es sin firmar int _lzcnt_u32 (sin firmar int a) por 32 bits entero y devuelve el recuento de ceros a la izquierda. Para 64 bits utilizar sin firmar __int64 _lzcnt_u64 (sin firmar __int64 a) 2

Encabezado de C++: immintrin.h

avatar de usuario
Tanguy

Probado 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;
}

  • ¿Has probado el rendimiento de tu clz64? No me sorprendería que toda esta ramificación lo haga más lento que la implementación del OP.

    – plamenko

    24 de septiembre de 2016 a las 13:13

  • Utilizar __builtin_clzll como una persona normal si desea compatibilidad con enteros de 64 bits en GNU C. Escribirlo de esta manera probablemente evitará que GCC use un único código de 64 bits. bsr o lzcnt en versiones de 64 bits. (Pero entonces también tendría disponible un MSVC intrínseco de 64 bits).

    – Peter Cordes

    17 oct 2020 a las 9:59

avatar de usuario
gregc

Hay dos elementos intrínsecos “_BitScanForward” y “_BitScanReverse”, que cumplen el 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 equivalentes de 64 bits “_BitScanForward64” y “_BitScanReverse64”.

Leer más aquí:

x86 intrínsecos en MSDN

  • ¿Has probado el rendimiento de tu clz64? No me sorprendería que toda esta ramificación lo haga más lento que la implementación del OP.

    – plamenko

    24 de septiembre de 2016 a las 13:13

  • Utilizar __builtin_clzll como una persona normal si desea compatibilidad con enteros de 64 bits en GNU C. Escribirlo de esta manera probablemente evitará que GCC use un único código de 64 bits. bsr o lzcnt en versiones de 64 bits. (Pero entonces también tendría disponible un MSVC intrínseco de 64 bits).

    – Peter Cordes

    17 oct 2020 a las 9:59

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad