@alignOf em Zig
O @alignOf retorna o alinhamento de memória de um tipo em bytes. O alinhamento determina em quais endereços de memória um valor desse tipo pode ser armazenado — o endereço deve ser divisível pelo alinhamento. Esse conceito é fundamental para desempenho e corretude em programação de sistemas.
Sintaxe
@alignOf(comptime T: type) comptime_int
O que faz
O @alignOf consulta o compilador para obter o requisito de alinhamento natural de um tipo em bytes. O alinhamento é sempre uma potência de 2. Tipos mal alinhados podem causar penalidades de desempenho em algumas arquiteturas ou até mesmo falhas de hardware em outras.
Parâmetros
- T (
type, comptime): O tipo cujo alinhamento será consultado. Pode ser qualquer tipo válido em Zig.
Valor de retorno
Retorna um comptime_int representando o alinhamento em bytes. O valor é sempre uma potência de 2.
Exemplos práticos
Exemplo 1: Alinhamento de tipos primitivos
const std = @import("std");
test "alinhamento de tipos primitivos" {
// Em arquiteturas de 64 bits típicas:
try std.testing.expect(@alignOf(u8) == 1);
try std.testing.expect(@alignOf(u16) == 2);
try std.testing.expect(@alignOf(u32) == 4);
try std.testing.expect(@alignOf(u64) == 8);
try std.testing.expect(@alignOf(f32) == 4);
try std.testing.expect(@alignOf(f64) == 8);
try std.testing.expect(@alignOf(bool) == 1);
}
Exemplo 2: Alinhamento de structs
const std = @import("std");
const Compacta = struct {
a: u8,
b: u8,
};
const ComPadding = struct {
a: u8,
b: u64, // Força alinhamento de 8
c: u8,
};
test "alinhamento de structs" {
// Struct alinha pelo campo de maior alinhamento
try std.testing.expect(@alignOf(Compacta) == 1);
try std.testing.expect(@alignOf(ComPadding) == 8);
// Tamanho inclui padding para manter alinhamento
std.debug.print("Tamanho de Compacta: {}\n", .{@sizeOf(Compacta)});
std.debug.print("Tamanho de ComPadding: {}\n", .{@sizeOf(ComPadding)});
}
Exemplo 3: Alocação alinhada personalizada
const std = @import("std");
fn alocarAlinhado(
allocator: std.mem.Allocator,
comptime T: type,
n: usize,
) ![]T {
const alinhamento = @alignOf(T);
std.debug.print("Alocando {} elementos de {s} com alinhamento {}\n", .{
n,
@typeName(T),
alinhamento,
});
return allocator.alloc(T, n);
}
const VetorSimd = @Vector(4, f32);
test "alocação com alinhamento SIMD" {
const allocator = std.testing.allocator;
// Vetores SIMD geralmente requerem alinhamento de 16 bytes
std.debug.print("Alinhamento de @Vector(4, f32): {}\n", .{
@alignOf(VetorSimd),
});
const dados = try alocarAlinhado(allocator, VetorSimd, 100);
defer allocator.free(dados);
// Verificar que os dados estão alinhados
try std.testing.expect(@intFromPtr(dados.ptr) % @alignOf(VetorSimd) == 0);
}
Casos de uso comuns
- Alocadores de memória: Garantir que blocos alocados respeitem o alinhamento necessário dos tipos armazenados.
- Operações SIMD: Verificar e garantir alinhamento correto para instruções vetoriais que exigem alinhamento de 16 ou 32 bytes.
- Interoperabilidade com C: Garantir que structs passadas para código C tenham o alinhamento esperado.
- Otimização de layout: Entender o padding inserido pelo compilador para otimizar o layout de structs.
- Hardware MMIO: Respeitar requisitos de alinhamento para acesso a registradores de hardware.
Builtins relacionados
- @sizeOf — Retorna o tamanho em bytes de um tipo
- @bitSizeOf — Retorna o tamanho em bits
- @alignCast — Converte alinhamento de ponteiro
- @ptrCast — Conversão entre tipos de ponteiro