Como você verifica se um pedaço alinhado de 16 u32
é consecutivo (e crescente)?
Por exemplo: [100, 101, 102, ..., 115]
é. E [100, 99, 3 ...]
não é.
Estou no AVX512f. Isto é o que tenho até agora:
Algo A:
* predefine DECREASE_U32, a u32x16 of [15,14,13,...0]
* let a = input + DECREASE_32 // wrapping is OK
* compare a to u32x16::splat(first_item(a))
* Return whether all true
Alterativa (Algo B)
* let b = copy of A
* permute the elements of b by one position
* let b = a-b
* Is b all 1's (except for 1st position)
Estou fazendo isso em Rust com a packed_simd
caixa, mas qualquer linguagem/pseudocódigo está bem. (Gostaria que houvesse uma operação SIMD para subtrair itens adjacentes.)
Acho que sua primeira ideia provavelmente será melhor se for feita dentro de um loop que possa amortizar o custo de carregamento de uma constante vetorial. AVX-512 pode fazer isso de forma eficiente.
Ou com uma carga vetorial e depois transmitir separadamente o elemento baixo com
vpbroadcastd
, ou com uma carga vetorial e uma carga de transmissão. por exemplovpaddd zmm16, zmm31, [rdi]{1to16}
/vpcmpeqd k1, zmm16, [rdi]
.Hmm, mas verificando se todos os elementos são verdadeiros, acho que talvez
kaddw
com uma constante1
e verifique se os 16 bits inferiores são zero comkortest
? Ou apenaskmov
para um registro inteiro para comparação,0xffff
como faríamos com SSE/AVXpmovmskb
. Eu tentei isso e o clang teve uma ideia melhor: compare se não é igual e verifique se a máscara é zero. (ou seja, verifique se todos os elementos são iguais, verificando se eles não são diferentes.) Isso permitekortest
a própria máscara. Apliquei a ideia do clang aos meus intrínsecos para que o GCC também pudesse fazer um conjunto melhor.Em C++:
Godbolt - asm do GCC e clang:
Então, ambos optam por carregar duas vezes em vez de
vpbroadcastd zmm1, xmm0
, pelo menos quando não estão em um loop, então a constante do vetor também precisa ser carregada.rodata
.Talvez se eu escrevesse de forma diferente, como
_mm512_broadcastd_epi32( _mm512_castsi512_si128(v))
, eles prefeririam uma carga, ao custo de um embaralhamento extra. (O que provavelmente é pior quando você tem uops de 512 bits em vôo, então as CPUs Intel desligam o vetor ALU na porta 1, deixando apenas as portas 0 e 5. https://agner.org/optimize/ e https://uops .info/ )Algo B - evitando uma constante vetorial não trivial
Talvez sua segunda maneira também possa ser feita de forma eficiente para
valignd
girar o vetor; a única constante vetorial necessária são todas aquelas que podem ser geradas de maneira um pouco mais barata (vpternlogd
) em vez de carregadas.Verificar a máscara de comparação provavelmente exigiria um
kmov
número inteiro para umand
+cmp
verificar todos os bits, exceto um, a menos que possamos usar o mesmo truque que o clang fez e organizar as coisas para que realmente queiramos que a máscara seja totalmente zero nos lugares que desejamos. Nesse caso,test eax, imm32
podemos verificar os bits que queremos, ignorando os que não queremos.O núcleo do meu código Rust atual agora é este código de macro:
Onde $scalar é
u32
, $simd éu32x16
e $decrease é o bloco [15, 14 ... 0]. A primeira parte do código verifica se o último elemento é 15 a mais que o primeiro (e cuida dos estouros).Pedi uma ferramenta inteligente para me ajudar a entender a montagem SIMD produzida. Diz: