Argonalyst

A importância dos registradores de vetor em CPUs x86-64

Argonalyst
25 July 2023

Se você remover a primeira palavra da string 'olá mundo', qual deve ser o resultado? Essa é a história de como descobrimos que a resposta pode ser a sua senha de root!

Introdução

Todos os CPUs x86-64 possuem um conjunto de registradores de vetor de 128 bits chamados de registradores XMM. Nunca é demais ter bits suficientes, então CPUs recentes expandiram a largura desses registradores para 256 bits e até mesmo 512 bits.

Os registradores estendidos de 256 bits são chamados de YMM e os registradores de 512 bits são chamados de ZMM.

Esses grandes registradores são úteis em muitas situações, não apenas para cálculos numéricos! Eles são até mesmo usados por funções da biblioteca C padrão, como strcmp, memcpy, strlen e assim por diante.

Vamos dar uma olhada em um exemplo. Aqui estão as primeiras instruções do strlen otimizado para AVX2 do glibc:

(gdb) x/20i __strlen_avx2 ... <__strlen_avx2+9>: vpxor xmm0,xmm0,xmm0 ... <__strlen_avx2+29>: vpcmpeqb ymm1,ymm0,YMMWORD PTR [rdi] <__strlen_avx2+33>: vpmovmskb eax,ymm1 ... <__strlen_avx2+41>: tzcnt eax,eax <__strlen_avx2+45>: vzeroupper <__strlen_avx2+48>: ret

A rotina completa é complicada e lida com muitos casos, mas vamos analisar esse caso simples. Tenha paciência, prometo que há um ponto!

O primeiro passo é inicializar ymm0 com zero, o que é feito simplesmente xorando xmm0 consigo mesmo.

VPXOR xmm0, xmm0, xmm0 > vpxor xmm0, xmm0, xmm0 vpcmpeqb ymm1, ymm0, [rdi] vpmovmskb eax, ymm1 tzcnt eax, eax vzeroupper

Aqui, rdi contém um ponteiro para nossa string, então vpcmpeqb verificará quais bytes em ymm0 correspondem à nossa string e armazenará o resultado em ymm1.

Como já definimos ymm0 para todos os bytes zero, apenas bytes nulos corresponderão.

vpcmpeqb ymm1, ymm0, rdi vpxor xmm0, xmm0, xmm0 > vpcmpeqb ymm1, ymm0, [rdi] vpmovmskb eax, ymm1 tzcnt eax, eax vzeroupper

Agora podemos extrair o resultado para um registrador de propósito geral como eax com vpmovmskb.

Qualquer byte nulo criará um bit 1 e qualquer outro valor criará um bit 0.

vpmovmskb eax, ymm1 vpxor xmm0, xmm0, xmm0 vpcmpeqb ymm1, ymm0, [rdi] > vpmovmskb eax, ymm1 tzcnt eax, eax vzeroupper

Encontrar o primeiro byte nulo agora é apenas uma questão de contar o número de bits zero à direita.

Essa é uma operação comum o suficiente para ter uma instrução para isso - tzcnt (Trailing Zero Count).

tzcnt eax, eax vpxor xmm0, xmm0, xmm0 vpcmpeqb ymm1, ymm0, [rdi] vpmovmskb eax, ymm1 > tzcnt eax, eax vzeroupper

Agora temos a posição do primeiro byte nulo, em apenas quatro instruções de máquina!

Você provavelmente pode imaginar quantas vezes o strlen está sendo executado em seu sistema agora, mas basta dizer que bits e bytes estão fluindo para esses registradores de vetor de todos os lugares do seu sistema constantemente.

Zerando Registradores Você pode ter percebido que eu perdi uma instrução, e essa é a vzeroupper. vzeroupper vpxor xmm0, xmm0, xmm0 vpcmpeqb ymm1, ymm0, [rdi] vpmovmskb eax, ymm1 tzcnt eax, eax > vzeroupper Você acertou, vzeroupper vai zerar os bits superiores dos registradores de vetor. A razão pela qual fazemos isso é porque, se misturarmos registradores XMM e YMM, os registradores XMM são automaticamente promovidos para a largura total. É um pouco como a promoção de inteiros em C. Isso funciona bem, mas processadores superscalares precisam rastrear dependências para saber quais operações podem ser paralelizadas. Essa promoção adiciona uma dependência nesses bits superiores, e isso causa atrasos desnecessários enquanto o processador espera por resultados que ele realmente não precisa. Esses atrasos são o que o glibc está tentando evitar com vzeroupper. Agora, quaisquer resultados futuros não dependerão desses bits, então evitamos com segurança esse gargalo!

O Arquivo de Registradores de Vetor Agora que sabemos o que vzeroupper faz, como ele faz isso? Seu processador não possui um único local físico onde cada registrador reside, ele possui o que é chamado de Arquivo de Registradores e uma Tabela de Alocação de Registradores. Isso é um pouco como gerenciar o heap com malloc e free, se você pensar em cada registrador como um ponteiro. A RAT acompanha qual espaço no arquivo de registradores está atribuído a qual registrador. Na verdade, quando você zera um registrador XMM, o processador não armazena esses bits em nenhum lugar - ele simplesmente define uma sinalização chamada de bit z na RAT. Essa sinalização pode ser aplicada às partes superiores e inferiores dos registradores YMM independentemente, então vzeroupper pode simplesmente definir o bit z e depois liberar quaisquer recursos atribuídos a ele no arquivo de registradores. Uma tabela de alocação de registradores (esquerda) e um arquivo de registradores físicos (direita).

Últimos vídeos

Confira os últimos vídeos publicados no canal

Argonalyst

Os EUA proibiram esses Robôs por medo de espionagem

Argonalyst

O verdadeiro motivo pelo qual querem controlar a IA

Argonalyst

IA da OpenAI escapou do Sandbox? O que REALMENTE aconteceu

Argonalyst

A maior virada da Inteligência Artificial começou... e vem da China

Argonalyst

o ALERTA de Satya Nadella que ASSUSTOU o mercado de IA

Argonalyst

GPT 5.6 SURPREENDE: OpenAI finalmente alcançou a Anthropic?

Argonalyst

Os novos modelos de IA estão decepcionando... e ninguém quer admitir isso

Argonalyst

Midjourney quer ESCANEAR humanos e o Open Source já rivaliza com Claude Opus

Argonalyst

Rio 3.5 e Fable 5: as duas polêmicas que expõem o futuro da IA

Argonalyst

Fim dos PCs como conhecemos: Nvidia, Microsoft e IA local vão mudar tudo

Argonalyst

O plano SECRETO das Big Techs para cobrar MUITO mais pela IA

Argonalyst

BOLHA da IA ou NOVA era de crescimento EXPONENCIAL? O mercado está dividido

Argonalyst

Nova IA da OpenAI traduz em TEMPO REAL e pode mudar o mundo dos negócios

Argonalyst

Spec Driven Development (SDD): a habilidade que vai separar quem SOBREVIVE à IA

Argonalyst

DeepSeek V4: o Open Source que está AMEAÇANDO GPT 5.5 e Opus 4.7