Pular para o conteúdo

Memória e layout

O que dá para medir: bytes por objeto, slots contra dicionário, o coletor, referências fracas e o que é assunto de linguagem compilada.

Alinhamento, padding, ABI e cache line são assunto de uma linguagem com layout fixo. Aqui o objeto é do interpretador, e a pergunta que muda o desenho de um programa é outra: quanto custa um objeto, e o que fazer a respeito.

slots: a diferença medida#

Sem slots, cada objeto carrega um dicionário próprio. Com slots, os campos viram uma lista indexada — medido neste repositório: 64% menos memória por objeto.

dataforge
adopt Arcane.Memoria as Mem

blueprint Compacta:
    slots x, y
    x := 1
    y := 2

blueprint Solta:
    x := 1
    y := 2

com := Mem.layout(Compacta)
sem := Mem.layout(Solta)

assert com["slots"] and not sem["slots"]
assert com["campos"] is ["x", "y"]
assert com["bytes"] smaller sem["bytes"]

comparacao := Mem.comparar_layout(Compacta, Solta)
assert comparacao["menor"] is "Compacta"
assert comparacao["economia_percentual"] bigger 20

Quanto ocupa um valor#

dataforge
adopt Arcane.Memoria as Mem

pequeno := Mem.tamanho([1, 2])
grande := Mem.tamanho([1, 2, "um texto bem maior para ocupar espaco"])

assert grande bigger pequeno
assert Mem.tamanho("x") bigger 0

A conta soma o objeto e tudo que só ele alcança, com guarda de ciclo — é a pergunta que interessa antes de guardar um milhão de linhas na memória.

O coletor, e o que ele não promete#

dataforge
adopt Arcane.Memoria as Mem

blueprint Conexao:
    nome := ""

c := spawn Conexao()
assert Mem.vivos(Conexao) is 1

c := void
assert Mem.vivos(Conexao) is 0

estat := Mem.estatisticas()
assert estat["ativo"]
assert len(estat["geracoes"]) is 3

O coletor libera quando quiser. Para o recurso que precisa fechar numa hora certa, a resposta é `Arcane.Posse`soltar() e P.com(…) rodam o finalizador naquele instante.

Referência fraca: observar sem segurar#

dataforge
adopt Arcane.Memoria as Mem

blueprint Sessao:
    id := 0

s := spawn Sessao()
fraca := Mem.fraca(s)
cache := Mem.mapa_fraco()
cache.definir(s, "dados caros")

assert fraca.viva() and cache.tamanho is 1

s := void
assert not fraca.viva()
assert cache.tamanho is 0          // o cache não segurou nada

O que é assunto de linguagem compilada#

ItemAqui
stack framesexistem como quadros de chamada: o stack trace os mostra, e o teto é de mil (a recursão de cauda não tem teto)
heap allocationtoda alocação é do Python; não há escolha entre pilha e heap
object layout, struct layout, enum layouto que existe é slots contra dicionário, e isso é medível (acima)
alignment, padding, cache linesnão se aplicam: não há layout fixo para alinhar
ABI, representação de ponteironão se aplicam: não há binário nem endereço exposto
endiannessexiste onde importa — em Arcane.Bytes.empacotar/desempacotar, na fronteira do arquivo e da rede
nullabilityé void, com ?? e ?.; e uma união String | Void diz isso no tipo
zero-sized typesnão existem: todo valor ocupa alguma coisa