imprimir una variable __m128i

Resuelto arunmoezhi asked hace 12 años • 4 respuestas

Estoy tratando de aprender a codificar usando elementos intrínsecos y a continuación hay un código que suma

compiler used: icc

#include<stdio.h>
#include<emmintrin.h>
int main()
{
        __m128i a = _mm_set_epi32(1,2,3,4);
        __m128i b = _mm_set_epi32(1,2,3,4);
        __m128i c;
        c = _mm_add_epi32(a,b);
        printf("%d\n",c[2]);
        return 0;
}

Recibo el siguiente error:

test.c(9): error: expression must have pointer-to-object type
        printf("%d\n",c[2]);

¿Cómo imprimo los valores en la variable cque es de tipo?__m128i

arunmoezhi avatar Nov 07 '12 01:11 arunmoezhi
Aceptado

Utilice esta función para imprimirlos:

#include <stdint.h>
#include <string.h>

void print128_num(__m128i var)
{
    uint16_t val[8];
    memcpy(val, &var, sizeof(val));
    printf("Numerical: %i %i %i %i %i %i %i %i \n", 
           val[0], val[1], val[2], val[3], val[4], val[5], 
           val[6], val[7]);
}

Divide 128 bits en 16 bits (o 32 bits) antes de imprimirlos.

Esta es una forma de dividir e imprimir de 64 bits si tiene soporte de 64 bits disponible:

#include <inttypes.h>

void print128_num(__m128i var) 
{
    int64_t v64val[2];
    memcpy(v64val, &var, sizeof(v64val));
    printf("%.16llx %.16llx\n", v64val[1], v64val[0]);
}

Nota: transmitir &vardirectamente a un MSVC int*o uint16_t*también funcionaría, pero esto viola el alias estricto y es un comportamiento indefinido. Usar memcpyes la forma estándar de hacer lo mismo y con una optimización mínima el compilador generará exactamente el mismo código binario.

askmish avatar Nov 06 '2012 18:11 askmish
  • Portátil en gcc/clang/ICC/MSVC, C y C++.
  • Totalmente seguro con todos los niveles de optimización: sin infracción de alias estricto UB.
  • imprimir en hexadecimal como elementos u8, u16, u32 o u64 ( según la respuesta de @ AG1 )
  • Imprime en orden de memoria (el elemento menos significativo primero, como _mm_setr_epiX). Invierta los índices de la matriz si prefiere imprimir en el mismo orden que usan los manuales de Intel, donde el elemento más significativo está a la izquierda (como _mm_set_epiX). Relacionado: Convención para mostrar registros vectoriales

Usar a __m128i*para cargar desde una matriz de intes seguro porque los __m128tipos están definidos para permitir el alias como ISO unsigned char*C. (por ejemplo, en los encabezados de gcc, la definición incluye __attribute__((may_alias))).

Lo contrario no es seguro (apuntar int*hacia parte de un __m128iobjeto). MSVC garantiza que es seguro, pero GCC/clang no. ( -fstrict-aliasingestá activado de forma predeterminada). A veces funciona con GCC/clang, pero ¿por qué arriesgarse? A veces incluso interfiere con la optimización; vea estas preguntas y respuestas . Consulte también ¿Es la `reinterpret_cast`ing entre el puntero vectorial SIMD de hardware y el tipo correspondiente un comportamiento indefinido?

Consulte GCC AVX _m256i cast to int array lleva a valores incorrectos para ver un ejemplo del mundo real de código de ruptura GCC que apunta a int*a __m256i.


(uint32_t*) &my_vectorviola las reglas de alias de C y C++ y no se garantiza que funcione de la manera esperada . Se garantiza que almacenar en una matriz local y luego acceder a ella será seguro. Incluso se optimiza con la mayoría de los compiladores, por lo que obtienes movq/ pextrqdirectamente desde xmm a registros enteros en lugar de un almacenamiento/recarga real , por ejemplo.

Fuente + salida asm en el explorador del compilador Godbolt : prueba de que se compila con MSVC, etc.

#include <immintrin.h>
#include <stdint.h>
#include <stdio.h>

#ifndef __cplusplus
#include <stdalign.h>   // C11 defines _Alignas().  This header defines alignas()
#endif

void p128_hex_u8(__m128i in) {
    alignas(16) uint8_t v[16];
    _mm_store_si128((__m128i*)v, in);
    printf("v16_u8: %x %x %x %x | %x %x %x %x | %x %x %x %x | %x %x %x %x\n",
           v[0], v[1],  v[2],  v[3],  v[4],  v[5],  v[6],  v[7],
           v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]);
}

void p128_hex_u16(__m128i in) {
    alignas(16) uint16_t v[8];
    _mm_store_si128((__m128i*)v, in);
    printf("v8_u16: %x %x %x %x,  %x %x %x %x\n", v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
}

void p128_hex_u32(__m128i in) {
    alignas(16) uint32_t v[4];
    _mm_store_si128((__m128i*)v, in);
    printf("v4_u32: %x %x %x %x\n", v[0], v[1], v[2], v[3]);
}

void p128_hex_u64(__m128i in) {
    alignas(16) unsigned long long v[2];  // uint64_t might give format-string warnings with %llx; it's just long in some ABIs
    _mm_store_si128((__m128i*)v, in);
    printf("v2_u64: %llx %llx\n", v[0], v[1]);
}

Si necesita portabilidad a C99 o C++03 o anterior (es decir, sin C11/C++11), elimine alignas()y utilice storeuen lugar de store. O usar __attribute__((aligned(16)))o __declspec( align(16) )en su lugar.

(Si está escribiendo código con intrínsecos, debería utilizar una versión reciente del compilador. Los compiladores más nuevos generalmente generan mejores conjuntos que los compiladores más antiguos, incluso para los intrínsecos SSE/AVX. Pero tal vez desee usar gcc-6.3 con -std=gnu++03C++03 modo para una base de código que no está lista para C++ 11 o algo así.)


Ejemplo de salida al llamar a las 4 funciones en

// source used:
__m128i vec = _mm_setr_epi8(1, 2, 3, 4, 5, 6, 7,
                            8, 9, 10, 11, 12, 13, 14, 15, 16);

// output:

v2_u64: 0x807060504030201 0x100f0e0d0c0b0a09
v4_u32: 0x4030201 0x8070605 0xc0b0a09 0x100f0e0d
v8_u16: 0x201 0x403 0x605 0x807  | 0xa09 0xc0b 0xe0d 0x100f
v16_u8: 0x1 0x2 0x3 0x4 | 0x5 0x6 0x7 0x8 | 0x9 0xa 0xb 0xc | 0xd 0xe 0xf 0x10

Ajuste las cadenas de formato si desea rellenar con ceros a la izquierda para lograr un ancho de salida consistente. Ver printf(3).

Peter Cordes avatar Oct 15 '2017 07:10 Peter Cordes

Sé que esta pregunta está etiquetada como C, pero también fue el mejor resultado de búsqueda cuando se buscaba una solución en C++ para el mismo problema.

Entonces, esta podría ser una implementación de C++:

#include <string>
#include <cstring>
#include <sstream>

#if defined(__SSE2__)
template <typename T>
std::string __m128i_toString(const __m128i var) {
    std::stringstream sstr;
    T values[16/sizeof(T)];
    std::memcpy(values,&var,sizeof(values)); //See discussion below
    if (sizeof(T) == 1) {
        for (unsigned int i = 0; i < sizeof(__m128i); i++) { //C++11: Range for also possible
            sstr << (int) values[i] << " ";
        }
    } else {
        for (unsigned int i = 0; i < sizeof(__m128i) / sizeof(T); i++) { //C++11: Range for also possible
            sstr << values[i] << " ";
        }
    }
    return sstr.str();
}
#endif

Uso:

#include <iostream>
[..]
__m128i x
[..]
std::cout << __m128i_toString<uint8_t>(x) << std::endl;
std::cout << __m128i_toString<uint16_t>(x) << std::endl;
std::cout << __m128i_toString<uint32_t>(x) << std::endl;
std::cout << __m128i_toString<uint64_t>(x) << std::endl;

Resultado:

141 114 0 0 0 0 0 0 151 104 0 0 0 0 0 0
29325 0 0 0 26775 0 0 0
29325 0 26775 0
29325 26775

Nota: existe una forma sencilla de evitarlo if (size(T)==1), consulte https://stackoverflow.com/a/28414758/2436175

Antonio avatar Sep 24 '2014 08:09 Antonio