Alignment (Alinhamento) em Zig — O que é e Como Usar
Definição
Alignment (alinhamento) em Zig refere-se à restrição de que certos tipos de dados devem estar posicionados em endereços de memória que são múltiplos de um valor específico. Por exemplo, um u32 tipicamente requer alinhamento de 4 bytes — seu endereço deve ser divisível por 4.
O alinhamento existe porque processadores acessam memória de forma mais eficiente (e às vezes apenas) quando os dados estão alinhados corretamente. Zig expõe o controle de alinhamento diretamente no sistema de tipos.
Por que Alignment Importa
- Performance: Acesso alinhado é mais rápido; acesso não-alinhado pode custar 2x ou mais.
- Correção: Em algumas arquiteturas (ARM, RISC-V), acesso não-alinhado causa falha de hardware.
- SIMD/Vetores: Operações vetoriais exigem alinhamento específico (16, 32 ou 64 bytes).
- Interop com C: O ABI C define alinhamentos que Zig precisa respeitar em
extern struct.
Exemplo Prático
Consultando Alinhamento
const std = @import("std");
pub fn main() void {
std.debug.print("u8: align = {}\n", .{@alignOf(u8)}); // 1
std.debug.print("u16: align = {}\n", .{@alignOf(u16)}); // 2
std.debug.print("u32: align = {}\n", .{@alignOf(u32)}); // 4
std.debug.print("u64: align = {}\n", .{@alignOf(u64)}); // 8
std.debug.print("f64: align = {}\n", .{@alignOf(f64)}); // 8
}
Ponteiros com Alinhamento Customizado
fn processarSIMD(dados: [*]align(16) const f32) void {
// Dados garantidamente alinhados a 16 bytes
// Operações SIMD seguras aqui
_ = dados;
}
pub fn main() void {
// Array com alinhamento customizado
var buffer align(16) = [_]f32{ 1.0, 2.0, 3.0, 4.0 };
processarSIMD(&buffer);
}
Alinhamento em Structs
const std = @import("std");
const SemPadding = extern struct {
a: u32, // offset 0, align 4
b: u32, // offset 4, align 4
c: u32, // offset 8, align 4
};
const ComPadding = extern struct {
a: u8, // offset 0, align 1
// 3 bytes de padding aqui!
b: u32, // offset 4, align 4
c: u8, // offset 8, align 1
// 3 bytes de padding aqui!
};
pub fn main() void {
std.debug.print("SemPadding: {} bytes\n", .{@sizeOf(SemPadding)}); // 12
std.debug.print("ComPadding: {} bytes\n", .{@sizeOf(ComPadding)}); // 12 (com padding!)
// Reordenando para economizar:
const Otimizado = extern struct {
b: u32, // offset 0
a: u8, // offset 4
c: u8, // offset 5
// 2 bytes de padding
};
std.debug.print("Otimizado: {} bytes\n", .{@sizeOf(Otimizado)}); // 8
}
@alignCast
fn processar(ptr: *align(1) u32) void {
// ptr pode estar em qualquer endereço
// Para operações que exigem alinhamento natural:
const alinhado: *u32 = @alignCast(ptr);
_ = alinhado;
}
Alocação com Alinhamento Específico
const std = @import("std");
pub fn main() !void {
const allocator = std.heap.page_allocator;
// Alocar com alinhamento customizado
const dados = try allocator.alignedAlloc(f32, 32, 1024);
defer allocator.free(dados);
// dados está alinhado a 32 bytes — ideal para AVX
std.debug.print("Endereço: {*}\n", .{dados.ptr});
}
Alinhamentos Comuns
| Tipo | Alinhamento típico (x86_64) |
|---|---|
u8, i8 | 1 byte |
u16, i16 | 2 bytes |
u32, i32, f32 | 4 bytes |
u64, i64, f64 | 8 bytes |
u128, i128 | 16 bytes |
SIMD @Vector(4, f32) | 16 bytes |
| Cache line | 64 bytes |
Armadilhas Comuns
- Ignorar alinhamento em interop C: Structs
externdevem respeitar o alinhamento do ABI C. Useextern structem vez destruct. @alignCastinseguro: Se o ponteiro não estiver realmente alinhado,@alignCastcausa comportamento indefinido em Release.- Padding desperdiça memória: Em arrays de structs com padding, o desperdício se multiplica. Reordene campos para minimizar padding.
- Assumir alinhamento: Diferentes arquiteturas têm diferentes requisitos. Não assuma que funciona em x86 significa que funciona em ARM.
Termos Relacionados
- Packed Struct — Structs sem padding
- Pointer Types — Tipos de ponteiro com alinhamento
- Vector/SIMD — Tipos vetoriais que exigem alinhamento
- Struct — Structs e layout de memória