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#
| Medida | Sem cache | Com cache |
|---|---|---|
| a fase de parse, 269 arquivos | 258,7 ms | 17,9 ms — 93% menos |
dataforge check exercicios (real, 269 arquivos) | 0,918 s | 0,524 s — 43% menos |
dataforge run num arquivo de 383 linhas | 131,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_nse o tamanho dele; - a versão da linguagem;
- o formato do que é guardado;
- e um resumo da própria implementação —
lexer.py,parser.py,ast_nodes.py,tokens.pyetipos_nomeados.py.
Falhar não pode custar nada#
| Se | Então |
|---|---|
| o arquivo do cache está corrompido | refaz o parse, sem levantar |
a versão de pickle é outra | refaz o parse |
| não há permissão de escrita | roda sem guardar |
| o processo morreu no meio da gravação | nã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#
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 arvoresdataforge 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.