Soy nuevo en las extensiones de vector C de GCC. Según el manual, el resultado de comparar un vector con otro en la forma (test = vec1 > vec2;) es que "test" contiene un 0 en cada elemento que es falso y un -1 en cada elemento que es verdadero.
Pero, ¿cómo verificar rápidamente si CUALQUIERA de las comparaciones de elementos fue verdadera? Y, además, ¿cómo saber cuál es el primer elemento para el que la comparación fue verdadera?
Por ejemplo, con:
vec1 = {1,1,3,1}; vec2 = {1,2,2,2}; test = vec1 > vec2;Quiero determinar si la "prueba" contiene alguna verdad (elementos distintos de cero). En este caso, quiero que "prueba" se reduzca a verdadero, porque existe un elemento para el cual vec1 es mayor que vec2 y, por lo tanto, un elemento en prueba que contiene -1.
Además, o alternativamente, quiero descubrir rápidamente QUÉ elemento no pasa la prueba. En este caso, este sería simplemente el número 2. Dicho de otra manera, quiero probar cuál es el primer elemento distinto de cero.
int hasAnyTruth = ...; // should be non-zero. "bool" works too since C99 int whichTrue = ...; // should contain 2, because test[2] == -1Me imagino que podríamos usar un comando simd de reducción y suma (?) para sumar todo en el vector en un número y comparar esa suma con 0, pero no sé cómo (o si hay una forma más rápida). Supongo que se necesita alguna forma de argmax para la segunda pregunta, pero nuevamente, no sé cómo indicarle a GCC que lo use en los vectores.
La extensión de vector de Clang hace un buen trabajo con any función.
#if defined(__clang__) typedef int64_t vli __attribute__ ((ext_vector_type(VLI_SIZE))); typedef double vdf __attribute__ ((ext_vector_type(VDF_SIZE))); #else typedef int32_t vsi __attribute__ ((vector_size (SIMD_SIZE))); typedef int64_t vli __attribute__ ((vector_size (SIMD_SIZE))); #endif static bool any(vli const & x) { for(int i=0; i<VLI_SIZE; i++) if(x[i]) return true; return false; }Asamblea
any(long __vector(4) const&): # @any(long __vector(4) const&) vmovdqa ymm0, ymmword ptr [rdi] vptest ymm0, ymm0 setne al vzeroupper ret Aunque pmovmskb aún podría ser una mejor opción, ptest sigue siendo una gran mejora con respecto a lo que hace GCC
any(long __vector(4) const&): cmp QWORD PTR [rdi], 0 jne .L5 cmp QWORD PTR [rdi+8], 0 jne .L5 cmp QWORD PTR [rdi+16], 0 jne .L5 cmp QWORD PTR [rdi+24], 0 setne al ret .L5: mov eax, 1 retGCC debería arreglar esto. Sin embargo, Clang no es óptimo para AVX512 .
Yo diría que any función es una función vectorial crítica, por lo que los compiladores deberían proporcionar una función integrada como lo hacen para la reproducción aleatoria (por ejemplo __builtin_shuffle para GCC y __builtin_shufflevector para clang) o el compilador debería ser lo suficientemente inteligente como para descubrir el código óptimo como lo hace Clang en menos para SSE y AVX pero no para AVX512.
De Mística:
_mm_movemask_epi8()
Es más portátil que las extensiones vectoriales GCC. Está estandarizado por Intel, por lo que funcionará en todos los principales compiladores: GCC, Clang, MSVC, ICC, etc...
Esto es lo que terminé usando en un caso:
#define V_EQ(v1, v2) \ ({ \ __typeof__ (v1) v_d = (v1) != (v2); \ __typeof__ (v_d) v_0 = { 0 }; \ memcmp (&v_d, &v_0, sizeof v_d) == 0; \ }) assert (V_EQ (v4ldblo, v4ldbli - 1));