Pular para o conteúdo

O cache de árvores

O lexer e o parser refaziam a mesma árvore a cada execução. Medido: 93% menos na fase de parse — e 4,4% num arquivo só, que também é o número.

A cada execução, o lexer e o parser refazem exatamente a mesma coisa a partir de um arquivo que não mudou. O cache guarda a árvore.

O número, e os dois números#

MedidaSem cacheCom cache
a fase de parse, 269 arquivos258,7 ms17,9 ms — 93% menos
dataforge check exercicios (real, 269 arquivos)0,918 s0,524 s — 43% menos
dataforge run num arquivo de 383 linhas131,8 ms~126 ms — 4,4%

A terceira linha é a desconfortável, e por isso está aqui: 76 ms dos 132 ms daquele comando são o import do próprio Python. O cache vale onde há muitos arquivos — o check de um projeto, a CI — e quase não aparece num script pequeno. Publicar só a primeira medida seria escolher a medida.

O cache dos 269 arquivos ocupa 1553 KB.

A chave é o que impede o desastre#

Um cache que devolve a árvore errada é pior que nenhum cache: o programa roda, e roda outra coisa. A chave carrega cinco coisas:

  • o caminho absoluto do arquivo;
  • o mtime_ns e o tamanho dele;
  • a versão da linguagem;
  • o formato do que é guardado;
  • e um resumo da própria implementaçãolexer.py, parser.py, ast_nodes.py, tokens.py e tipos_nomeados.py.

Falhar não pode custar nada#

SeEntão
o arquivo do cache está corrompidorefaz o parse, sem levantar
a versão de pickle é outrarefaz o parse
não há permissão de escritaroda sem guardar
o processo morreu no meio da gravaçãonão há arquivo pela metade: escreve ao lado e renomeia, e os.replace é atômico no POSIX e no Windows

Uma otimização nunca pode ser um motivo de erro. Toda leitura do cache está num try, e toda falha cai no caminho normal.

Ligar, desligar, limpar#

bash
DATAFORGE_SEM_CACHE=1 dataforge check src/   # desliga, para medir
DATAFORGE_CACHE=/tmp/meu dataforge check src/ # outra pasta
dataforge clean                               # apaga o cache de arvores

dataforge clean sempre apaga o cache de árvores, e diz quantos arquivos tirou. Deixá-lo para trás faria o clean mentir sobre o que limpou — e é justamente o lugar onde um artefato velho engana.

A prova de que ele é seguro não é o desenho: é o teste que roda exercícios do repositório com a árvore do cache e sem ela, e compara a saída caractere por caractere — a mesma rede de segurança do compilador de fechamentos.