Cheatsheet: Pool de Objetos em Zig

Pool de Objetos em Zig

O padrão Pool de Objetos mantém uma coleção de objetos pré-alocados e reutilizáveis, evitando o custo de alocação e desalocação frequente. Em Zig, com controle explícito de memória, este padrão é especialmente útil para aplicações de alta performance, servidores e sistemas embarcados.

Quando Usar

  • Servidores que criam/destroem conexões frequentemente
  • Game loops com criação/destruição de entidades
  • Pools de buffers para I/O
  • Sistemas embarcados com memória limitada
  • Evitar fragmentação de memória

Implementação Básica

const std = @import("std");

fn Pool(comptime T: type, comptime tamanho: usize) type {
    return struct {
        const Self = @This();

        itens: [tamanho]T = undefined,
        disponiveis: [tamanho]bool = [_]bool{true} ** tamanho,
        inicializador: *const fn () T,

        pub fn init(inicializador: *const fn () T) Self {
            var self = Self{
                .inicializador = inicializador,
            };
            for (&self.itens) |*item| {
                item.* = inicializador();
            }
            return self;
        }

        pub fn adquirir(self: *Self) ?*T {
            for (self.disponiveis, 0..) |*disp, i| {
                if (disp.*) {
                    disp.* = false;
                    return &self.itens[i];
                }
            }
            return null; // pool esgotado
        }

        pub fn liberar(self: *Self, ptr: *T) void {
            const inicio = @intFromPtr(&self.itens[0]);
            const endereco = @intFromPtr(ptr);
            const indice = (endereco - inicio) / @sizeOf(T);
            if (indice < tamanho) {
                self.itens[indice] = (self.inicializador)(); // resetar
                self.disponiveis[indice] = true;
            }
        }

        pub fn disponiveisCount(self: *const Self) usize {
            var count: usize = 0;
            for (self.disponiveis) |d| {
                if (d) count += 1;
            }
            return count;
        }
    };
}

const Buffer = struct {
    dados: [1024]u8 = undefined,
    tamanho: usize = 0,

    fn criar() Buffer {
        return Buffer{};
    }
};

pub fn main() void {
    var pool = Pool(Buffer, 16).init(Buffer.criar);

    // Adquirir buffer do pool
    if (pool.adquirir()) |buf| {
        buf.dados[0] = 'A';
        buf.tamanho = 1;

        std.debug.print("Buffer adquirido, disponíveis: {d}\n", .{pool.disponiveisCount()});

        // Devolver ao pool quando terminar
        pool.liberar(buf);
        std.debug.print("Buffer liberado, disponíveis: {d}\n", .{pool.disponiveisCount()});
    }
}

Pool com Allocator Dinâmico

const std = @import("std");

fn PoolDinamico(comptime T: type) type {
    return struct {
        const Self = @This();

        livres: std.ArrayList(*T),
        allocator: std.mem.Allocator,
        total_criados: usize = 0,
        max_tamanho: usize,

        pub fn init(allocator: std.mem.Allocator, max: usize) Self {
            return .{
                .livres = std.ArrayList(*T).init(allocator),
                .allocator = allocator,
                .max_tamanho = max,
            };
        }

        pub fn deinit(self: *Self) void {
            for (self.livres.items) |item| {
                self.allocator.destroy(item);
            }
            self.livres.deinit();
        }

        pub fn adquirir(self: *Self) !*T {
            if (self.livres.popOrNull()) |item| {
                return item;
            }
            // Criar novo se abaixo do limite
            if (self.total_criados < self.max_tamanho) {
                const novo = try self.allocator.create(T);
                novo.* = std.mem.zeroes(T);
                self.total_criados += 1;
                return novo;
            }
            return error.PoolEsgotado;
        }

        pub fn liberar(self: *Self, item: *T) !void {
            item.* = std.mem.zeroes(T); // resetar
            try self.livres.append(item);
        }
    };
}

Quando Evitar

  • Objetos pequenos e baratos de criar (overhead do pool supera o benefício)
  • Quando o tamanho do pool é difícil de prever
  • Objetos com estado complexo difícil de resetar
  • Aplicações onde memória não é gargalo

Veja Também

Continue aprendendo Zig

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