Antes da primeira linha
As sete fases que rodam antes da sua primeira instrução — e quanto cada adopt custou na partida.
Um programa não começa na primeira linha. Antes dela o `comptime` rodou numa caixa sem E/S, os adopt carregaram módulos e as declarações de topo foram içadas.
Nada disso era visível — e "por que a partida demora 400 ms?" não tinha como ser respondido sem cronometrar à mão.
adopt Arcane.Inicio as I
fases := I.fases()
assert [f["fase"] cycle f in fases] is
["lexer", "parser", "comptime", "hoisting", "adopt", "programa", "defer"]| Fase | O que acontece |
|---|---|
lexer | o texto vira tokens, com linha e coluna em cada um |
parser | os tokens viram a árvore; um erro de sintaxe para aqui |
comptime | os blocos rodam numa caixa sem E/S, e o que decidem vira constante antes de o programa existir |
hoisting | as declarações de topo são içadas: uma ação pode ser chamada antes da linha em que foi escrita |
adopt | cada módulo é resolvido e carregado, na ordem do arquivo — é aqui que a partida costuma ser gasta |
programa | a primeira instrução de topo finalmente roda |
defer | os defer de topo rodam no fim, na ordem inversa |
Onde a partida foi gasta#
adopt Arcane.Inicio as I
adopt Arcane.Math as M
// cada adopt e cronometrado — e a pergunta aparece quando alguem
// NAO desconfiava, entao a medida e sempre ligada
assert len(I.adocoes()) bigger_eq 2
assert I.relatorio()["adocoes"] bigger_eq 2
assert "mais_caro" in I.relatorio() 4 modulo(s) carregado(s) em 38.42 ms
Arcane.Cortex 24.108 ms 62.7%
Arcane.Database 9.902 ms 25.8%
Arcane.Math 2.914 ms 7.6%
Arcane.Text 1.496 ms 3.9%Globais, e a ordem#
steady declara constante; a ordem de inicialização é a do arquivo, e os adopt acontecem onde estão escritos. O ciclo de import é erro do `check`, e não estouro em execução — a busca é em largura, para achar o ciclo mais curto.
E a destruição existe: um defer no nível de topo roda no fim do programa, na ordem inversa. É como se fecha um arquivo ou uma conexão sem depender de ninguém lembrar.