Para evitar penalidades de transição, o compilador pode adicionar automaticamente chamadas para VZEROUPPER (limpa os 128 bits superiores) ou VZEROALL (limpa todo o registrador YMM) com o parâmetro -mvzeroupper, ou o programador pode fazê-lo manualmente. Se você não estiver usando nenhuma biblioteca SSE externa, e tiver certeza de que tudo que você programa é habilitado para VEX e compilado com extensões AVX habilitadas, você pode instruir o compilador a evitar adicionar chamadas VZEROUPPER, com: -mno-vzeroupper
Mover dados dos registradores AVX pra lá e pra cá pode ser custoso. Em alguns casos, se você tem alguns dados armazenados em estruturas lineares, enviar esses dados para vetores AVX, executar algumas operações e recuperar esses dados pode ser mais custoso que simplesmente realizar os cálculos linearmente.
Algum tempo atrás, eu tentei simular um pouco de física em um jogo, Codingame's Poker Chip Race, com AVX. Esse jogo é baseado em N entidades circulares colidindo em um espaço 2D. Então, naquele cenário eu precisava calcular até N*(N-1)/2 possíveis colisões. Minha primeira tentativa envolveu ter classes de entidade normais (com posição, raio, etc) e para o cálculo de colisão eu embaralhei todas as possíveis colisões de entidades em vetores AVX, as calculei em AVX e as retornei de volta para as classes de entidade. Quando terminei, os resultados que obtive foram medíocres, com uma melhoria não superior à 30% em relação ao código linear, com muita sobrecarga e um código difícil de manter. Depois de usar um profiler eu detectei que 90% do tempo de CPU estava sendo usado em carregamento e descarregamento de dados.