Publicado em 25 de setembro de 2026· 7 min de leitura

    Go 1.27 lança API SIMD portátil experimental para amd64, arm64 e wasm

    Capa do post sobre a API SIMD portátil do Go, com o título em branco sobre fundo navy e uma malha de nós conectados em tons de dourado

    Go 1.27 introduz um pacote simd experimental que executa operações SIMD portáteis em amd64, arm64 e wasm com a mesma fonte. A API cobre AVX, AVX2 e AVX512 em amd64, NEON em arm64 e as instruções SIMD do wasm, com emulação em plataformas sem suporte. É a primeira interface SIMD de Go independente de arquitetura e tamanho de vetor, segundo a nota oficial de David Chase e Junyang Shao no Go Blog.

    Por que o SIMD portátil é difícil em Go

    Antes desses pacotes, a única forma de acessar SIMD em Go era escrevendo assembly. Isso só valia a pena para kernels computacionais realmente críticos, e muito software que poderia se beneficiar simplesmente deixava parte da CPU ociosa. Go 1.26 introduziu uma API SIMD para amd64 no pacote archsimd; Go 1.27 adicionou APIs para arm64, especificamente NEON, e para wasm.

    O problema central é a variação entre plataformas. Ela aparece em pelo menos três dimensões. A primeira é o tamanho do vetor: wasm, PowerPC e s390x usam 128 bits fixos; amd64 tem 128, 256 e 512 bits; loong64 tem 128 e 256 bits. Riscv64 suporta vetores de tamanho não especificado entre 128 e 65536 bits, limitado a potências de 2. Arm64 combina um tamanho fixo de 128 bits no NEON e um variável de 128 a 2048 bits no SVE, também em potências de 2.

    A segunda dimensão é o mascaramento. Algumas variantes não têm registradores de máscara e fazem masking com bitmasks de vetor e operações booleanas; é o caso de wasm, AVX, AVX2 e NEON. AVX512 e RVV têm registradores de máscara dedicados, com um bit por elemento. SVE aloca um bit por byte do vetor, e o bit menos significativo de cada elemento governa a operação mascarada. AVX2 suporta loads e stores mascarados, mas usando um vetor comum como máscara, com o bit mais significativo governando.

    A terceira dimensão são as operações em si. Primitivas de rearranjo de elementos variam entre exigir entradas constantes ou variáveis, operações criptográficas diferem entre arquiteturas, e até aritmética básica tem lacunas — wasm, por exemplo, não tem comparações para vetores de inteiros de 64 bits. Em amd64, ainda é preciso checar se a instância suporta AVX, AVX2 ou AVX512; em arm64, se é NEON ou SVE e, no caso de SVE, o tamanho e a variante entre SVE, SVE2 e SVE2.1.

    O archsimd foi desenhado para ser o mais uniforme possível, mas muitas dessas peculiaridades permanecem. Isso torna desenhar, escrever e testar código SIMD multiplataforma oneroso, e a uniformização tem limite sem comprometer eficiência.

    Como o pacote simd abstrai as diferenças

    O novo pacote simd remove vetores de tamanho fixo do type system e suporta apenas operações que estão na interseção de todas as plataformas. Onde a interseção tem lacunas, o pacote as preenche com emulação eficiente em termos de outras instruções SIMD. A API é baseada vagamente no Highway para C++.

    O objetivo do conjunto de operações é ser adequado para algoritmos de processamento de dados que se beneficiam de vetorização sem depender de um tamanho específico, ser tão eficiente quanto assembly quando as operações do código correspondem ao hardware, ser emulado da melhor forma possível nos demais casos e permanecer fácil de ler e entender. Em plataformas que não têm instruções SIMD ou não têm suporte no archsimd, todas as operações são emuladas, então o código continua executando.

    Os tipos de vetor são plurais capitalizados, como simd.Uint8s e simd.Float32s. Vetores são carregados de slices e armazenados de volta em slices. Para usar o pacote, basta definir GOEXPERIMENT=simd, o mesmo mecanismo do archsimd experimental.

    O exemplo do Go Blog para produto interno mostra o padrão de uso:

    // innerProduct returns the inner product of x and y.
    func innerProduct(x, y []float32) float32 {
    	var a simd.Float32s
    	var i int
    	for i = 0; i < len(x)-a.Len()+1; i += a.Len() {
    		u := simd.LoadFloat32s(x[i : i+a.Len()])
    		v := simd.LoadFloat32s(y[i : i+a.Len()])
    		a = u.MulAdd(v, a)
    	}
    	if i < len(x) {
    		u, _ := simd.LoadFloat32sPart(x[i:])
    		v, _ := simd.LoadFloat32sPart(y[i:])
    		a = u.MulAdd(v, a)
    	}
    	return sum(a)
    }
    
    // sum returns scalar sum of elements of x.
    func sum(x simd.Float32s) float32 {
    	s := make([]float32, x.Len())
    	x.Store(s)
    	var r float32
    	for _, e := range s {
    		r += e
    	}
    	return r
    }
    

    O loop principal processa blocos de a.Len() elementos e o restante é tratado com LoadFloat32sPart, que lida com slices menores que o tamanho do vetor.

    Operações suportadas em Go 1.27

    A API do pacote simd em Go 1.27 inclui funções de load e broadcast em nível de pacote, seleção condicional com IfElse(mask MaskWs, y V) V, operações booleanas e de mascaramento vetorial, e CarrylessMultiplyEven(y V) V, entre outras. As comparações SIMD produzem valores de máscara específicos à largura do elemento: comparações de Int8s produzem Mask8s, por exemplo. Essas máscaras podem ser usadas para selecionar e filtrar vetores.

    A decisão de restringir a API à interseção entre plataformas é deliberada. O pacote não tenta expor toda operação que existe em alguma arquitetura; em vez disso, foca no subconjunto comum e emula o restante. Isso permite que o mesmo código rode em amd64, arm64 e wasm sem bifurcações por sistema operacional ou feature detectada em runtime.

    O que muda na prática

    A principal mudança é a redução da barreira de entrada para SIMD em Go. Em vez de escrever assembly por arquitetura ou lidar com as peculiaridades remanescentes do archsimd, um único código pode ser compilado para múltiplas plataformas. Em plataformas sem SIMD, o código continua executando por emulação, o que evita a manutenção de dois caminhos distintos.

    SIMD acelera tarefas computacionalmente intensivas como criptografia, processamento de dados e IA, as três áreas citadas na nota como beneficiárias típicas. O garbage collector Green Tea do próprio Go já usa SIMD para acelerar a varredura de memória em busca de objetos vivos, o que indica o potencial da técnica dentro do ecossistema da linguagem.

    A promessa de near-asm-performance vale quando as operações do código correspondem diretamente às instruções do hardware. Nesses casos, o pacote busca eficiência equivalente à do assembly. Quando a operação não existe na plataforma, entra a emulação em termos de outras instruções SIMD, com custo maior.

    Limitações da primeira release

    A primeira release experimental tem restrições importantes que a nota oficial explicita. ReduceSum não está disponível em Go 1.27 porque não existe uma forma comum de somar todos os elementos de um vetor entre as plataformas suportadas; a operação aparecerá na próxima release, quando poderá substituir o sum escalar do exemplo.

    O pacote é experimental e exige GOEXPERIMENT=simd, o que significa que a API pode mudar sem garantia de compatibilidade. A limitação à interseção entre plataformas também deixa de fora operações específicas de uma arquitetura — como certas primitivas criptográficas ou rearranjos mais flexíveis — mesmo quando o hardware as suporta. Nesses casos, o ganho potencial de instruções nativas não é aproveitado.

    Outra limitação prática é que a emulação em plataformas sem suporte a SIMD garante correção e execução, mas não o desempenho próximo ao assembly. Para código que roda majoritariamente em plataformas sem SIMD, a vantagem de usar o pacote é pequena.

    Quando adotar o pacote simd

    Faz sentido experimentar o pacote simd em kernels de processamento numérico, criptografia, processamento de dados ou manipulação de vetores que hoje justificariam escrever assembly Go ou usar o archsimd. Bibliotecas distribuídas para múltiplas arquiteturas se beneficiam diretamente do modelo write-once.

    Não faz sentido adotar em código que não é gargalo de CPU, em operações que dependem de instruções fora da interseção entre plataformas, ou em produção que exige APIs estáveis. Também não é a escolha certa quando o código precisa extrair o máximo de uma arquitetura específica, como instruções criptográficas dedicadas ou rearranjos com entrada variável que só existem em algumas variantes SIMD.

    O pacote simd representa uma mudança de postura da linguagem: em vez de expor o hardware e deixar a portabilidade como problema do desenvolvedor, ele define um subconjunto comum e trata as diferenças por emulação. Para kernels que cabem nesse subconjunto, é um caminho mais simples que o assembly e mais portátil que o archsimd. Para o restante, as APIs dependentes de arquitetura continuam disponíveis.

    Compartilhar:

    Leia também

    Fontes

    Maxwell Diniz
    Maxwell Diniz

    Fundador da DEVDINIZ

    Software em produção

    51 ferramentas gratuitas, direto no navegador

    Utilitários para CPF, CNPJ, JWT, JSON, regex, cores e mais. Processamento local, sem cadastro e sem conta — a mesma engenharia que sustenta o que escrevemos aqui.