Vector SIMD em Zig — O que é e Como Usar

Vector SIMD em Zig — O que é e Como Usar

Definição

Vetores SIMD (Single Instruction, Multiple Data) em Zig são tipos que permitem executar a mesma operação em múltiplos dados simultaneamente, aproveitando instruções vetoriais da CPU como SSE, AVX (x86) ou NEON (ARM). O tipo @Vector(N, T) cria um vetor de N elementos do tipo T, e operações sobre ele são mapeadas diretamente para instruções SIMD do hardware.

Diferente de bibliotecas SIMD externas, vetores SIMD são cidadãos de primeira classe em Zig — suportam operadores aritméticos, comparações e indexação nativamente.

Por que Vetores SIMD Importam

  1. Performance: Processar 4, 8, 16 ou mais elementos em uma única instrução de CPU.
  2. Portabilidade: O compilador traduz para as instruções SIMD disponíveis na arquitetura-alvo.
  3. Sem assembly: Acesso a SIMD com a mesma sintaxe de Zig normal.
  4. Auto-vetorização: O LLVM backend pode otimizar ainda mais o código vetorial gerado.

Exemplo Prático

Operações Básicas com Vetores

const std = @import("std");

pub fn main() void {
    const a: @Vector(4, f32) = .{ 1.0, 2.0, 3.0, 4.0 };
    const b: @Vector(4, f32) = .{ 5.0, 6.0, 7.0, 8.0 };

    // Operações elemento a elemento
    const soma = a + b;          // { 6.0, 8.0, 10.0, 12.0 }
    const produto = a * b;       // { 5.0, 12.0, 21.0, 32.0 }

    // Escalar broadcast
    const dobro = a * @as(@Vector(4, f32), @splat(2.0));

    std.debug.print("Soma: {d}\n", .{soma});
    std.debug.print("Produto: {d}\n", .{produto});
    std.debug.print("Dobro: {d}\n", .{dobro});
}

Soma de Array com SIMD

const std = @import("std");

fn somaSimd(dados: []const f32) f32 {
    const VEC_LEN = 4;
    var acumulador: @Vector(VEC_LEN, f32) = @splat(0.0);

    var i: usize = 0;
    while (i + VEC_LEN <= dados.len) : (i += VEC_LEN) {
        const chunk: @Vector(VEC_LEN, f32) = dados[i..][0..VEC_LEN].*;
        acumulador += chunk;
    }

    // Reduzir vetor para escalar
    var total: f32 = @reduce(.Add, acumulador);

    // Processar elementos restantes
    while (i < dados.len) : (i += 1) {
        total += dados[i];
    }

    return total;
}

pub fn main() void {
    const dados = [_]f32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    const resultado = somaSimd(&dados);
    std.debug.print("Soma SIMD: {d}\n", .{resultado}); // 55.0
}

Comparação Vetorial

const std = @import("std");

pub fn main() void {
    const a: @Vector(4, i32) = .{ 10, 20, 30, 40 };
    const b: @Vector(4, i32) = .{ 15, 15, 35, 35 };

    // Comparação retorna vetor de bool
    const maior = a > b; // { false, true, false, true }

    // Selecionar baseado na comparação
    const resultado = @select(i32, maior, a, b);
    // { 15, 20, 35, 40 } — o maior de cada par

    std.debug.print("Resultado: {}\n", .{resultado});
}

Operações Disponíveis

OperaçãoDescrição
+, -, *, /Aritmética elemento a elemento
>, <, ==, !=Comparação (retorna vetor de bool)
@reduce(.Add, v)Reduz vetor a escalar com soma
@splat(valor)Cria vetor com todos elementos iguais
@select(T, mask, a, b)Seleciona elementos por máscara
@shuffleReorganiza elementos entre vetores

Armadilhas Comuns

  • Tamanho deve ser potência de 2: Vetores com tamanhos como 3 ou 5 podem não mapear para instruções SIMD reais.
  • Alinhamento: Vetores SIMD exigem alinhamento específico. O compilador gerencia isso automaticamente em variáveis locais.
  • Fallback escalar: Se a CPU não suportar SIMD para o tamanho escolhido, o compilador gera código escalar equivalente (mais lento).
  • Elementos restantes: Quando o array não é múltiplo do tamanho do vetor, trate os elementos restantes em um loop escalar.

Termos Relacionados

Tutoriais Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.