Nova biblioteca intrínseca x86 SIMD - immintrin debug

A cada nova geração de processadores Intel, novas e cada vez mais complexas instruções vetoriais estão surgindo. Embora o comprimento do vetor (512 bits) não aumente no futuro próximo, novos tipos de dados e tipos de instruções aparecerão. Por exemplo, quem pode entender rapidamente o que uma intrínseca (e a instrução correspondente do processador) faz?

Lógica ternária bit a bit que fornece a capacidade de implementar qualquer função binária de três operandos; a função binária específica é especificada pelo valor em imm8.

__m512i _mm512_mask_ternarylogic_epi32 (__m512i src, __mmask8 k, __m512i a, __m512i b, int imm8) FOR j := 0 to 15 i := j*32 IF k[j] FOR h := 0 to 31 index[2:0] := (src[i+h] << 2) OR (a[i+h] << 1) OR b[i+h] dst[i+h] := imm8[index[2:0]] ENDFOR ELSE dst[i+31:i] := src[i+31:i] FI ENDFOR dst[MAX:512] := 0 

OK, digamos que descobrimos como isso funciona. O próximo nível de complexidade é o código de depuração que usa intensamente esses intrínsecos.

Quem usa regularmente intrínsecos conhece um site muito útil - Guia de intrínsecos da Intel . Se você observar atentamente como ele funciona, é fácil perceber que o front-end javascript baixa o arquivo data-3.xxxml, que descreve em detalhes todas as intrínsecas, com código semelhante ao Matlab. (Por exemplo, o que eu copiei no título da postagem.)

Mas quando usamos intrínsecos para acelerar o código, escrevemos não no Matlab, mas em C e C ++! Há três meses, um cliente me perguntou se havia uma implementação de intrínseca de vetor em C para depuração, e eu decidi escrever um analisador que traduz o código do Guia de Intrínseca para C. Acontece que uma biblioteca implementa quase todos os intrínsecos para que você possa usar um depurador passo a passo ( ou adicione debug printf).

Por exemplo, uma operação de um título de postagem se transforma em

 for (int j = 0; j <= 15; j++) { if (k & (1 << j)) { for (int h = 0; h <= 31; h++) { int index = ((((src_vec[j] & (1 << h)) >> h) << 2) | (((a_vec[j] & (1 << h)) >> h) << 1) | ((b_vec[j] & (1 << h)) >> h)) & 0x7; dst_vec[j] = (dst_vec[j] & ~(1 << h)) | ((((imm8 & (1 << index)) >> index)) << h); } } else { dst_vec[j] = src_vec[j]; } } 

Verdade, isso é muito mais compreensível? Na verdade não? Bem, eu apenas escolhi uma função complexa como exemplo. Geralmente, quando você depura códigos com intrínsecas (por exemplo, DSP), é necessário ter em mente o algoritmo e os recursos de cada instrução. Dado que as instruções funcionam com vetores longos e os algoritmos DSP geralmente são baseados em matemática séria, minha cabeça não lida - não há memória e concentração de curto prazo suficientes. Suspeito que não estou sozinho - várias vezes até pensei ter encontrado um bug nas instruções. Então, é claro, toda vez que eu estava errado, não funcionava para abrir um novo bug do FDIV. Mas se nesses casos eu pudesse depurar passo a passo as instruções, eu entenderia imediatamente sob quais condições um valor aparece no componente do meu vetor que eu não esperava.

Os clientes me disseram que usam essa biblioteca para depurar funções individuais com intrínsecas do AVX-512 em um laptop que suporta apenas o AVX2. Obviamente, o Intel SDE é muito mais adequado para isso - porque imita com extrema precisão todos os conjuntos de instruções. Eu tenho um conjunto de testes de unidade (também gerados automaticamente) que, para cada intrínseco da biblioteca, compara o resultado de seu trabalho com o resultado da execução da instrução assembler correspondente. Como convém aos testes de unidade, a maioria funciona como esperado. Mas alguns intrínsecos de depuração de ponto flutuante (precisão dupla e única) nem sempre funcionam 100% corretamente. Eu diria que às vezes é um tipo de matemática rápida . E existem diferentes mecanismos de arredondamento! O IEE754 possui muitas sutilezas ...

Há outro recurso importante do uso de depuração de immintrin em vez do SDE (que eu não aprovo de forma alguma, mas não consigo parar). Se você compilar gcc ou clang com a opção, por exemplo, -march = nehalem , gcc e clang retornarão vetores de 512 bits das funções na pilha a partir das funções, e o ICC ainda os retornará ao ZMM0. Portanto, o compilador Intel não pode ser usado nesse modo. E o gcc tem uma opção útil -Og , que ajuda na depuração, inclusive na depuração do immintrin.

Existem várias intrínsecas cuja ação principal é alterar o conteúdo do registro, por exemplo, ou sinalizadores. Eu não implementei essas instruções. Bem, enquanto meu analisador não está totalmente pronto, a implementação de cerca de 10% dos intrínsecos ainda não está disponível.

O uso da depuração immintrin é muito simples - você não precisa alterar o código fonte, mas precisa adicionar compilação condicional para incluir immintrin_dbg.h em vez de immintrin.h no caso de compilação de depuração.

Você pode baixá-lo no github .

Source: https://habr.com/ru/post/pt450964/


All Articles