A diferença é real, ou é ruído?
Mann-Whitney sobre as amostras: o teste que impede a ferramenta de inventar ganho — e por que não é o teste t.
Comparar uma ação com ela mesma não pode dar "3% mais rápida". É o que separa medição de superstição, e é o primeiro teste do arquivo de testes.
adopt Arcane.Perfil as P
action consulta():
yield sum(range(400))
// a MESMA acao dos dois lados: a diferenca e ruido, e a ferramenta
// tem de dizer isso
v := P.comparar(consulta, consulta, {"amostras": 40})
assert v["mais_rapido"] is "empate"
assert not v["significativo"]adopt Arcane.Perfil as P
action rapida():
yield sum(range(400))
action lenta():
yield sum(range(9000))
d := P.comparar(rapida, lenta, {"amostras": 30})
assert d["significativo"]
assert d["mais_rapido"] is "a"
assert d["fator"] bigger 2
assert d["p_valor"] smaller 0.05Por que Mann-Whitney, e não o teste t#
Tempo de execução não é normal: tem cauda longa à direita, piso duro à esquerda (nada roda em tempo negativo) e picos de escalonamento do sistema. Um teste t supõe normalidade e responde com confiança sobre uma suposição falsa.
O U de Mann-Whitney não supõe nada sobre a forma — ele compara ordens. E a correção de empates importa quando o relógio tem resolução grossa e muitas amostras dão o mesmo valor.
| Campo | O que diz |
|---|---|
p_valor | a chance de ver esta diferença se as duas fossem iguais |
significativo | p_valor abaixo de alfa (padrão 0,05) |
sobreposicao | o tamanho do efeito: a chance de um sorteio de A ser menor que um de B. É o que p não diz |
fator | quantas vezes, na mediana — e só quando é significativo |
a, b | a distribuição completa de cada lado |
Regressão: piorou desde a semana passada?#
Um número sozinho não responde isso. A linha de base fica num arquivo, e o conferir compara.
adopt Arcane.Perfil as P
adopt Arcane.IO as IO
adopt Arcane.OS as OS
base := $"{OS.temp_dir()}/df-doc-{randint(100000, 999999)}.json"
P.guardar("consulta", {"p95": 1.0, "p50": 1.0, "media": 1.0}, base)
// tres vezes mais lento: regrediu
ruim := P.conferir("consulta", {"p95": 3.0, "p50": 3.0, "media": 3.0},
{"arquivo": base, "tolerancia": 0.2})
assert ruim["regrediu"] and ruim["fator"] is 3.0
// 5% mais lento, com 20% de tolerancia: e ruido de maquina
ok := P.conferir("consulta", {"p95": 1.05, "p50": 1.05, "media": 1.05},
{"arquivo": base, "tolerancia": 0.2})
assert not ok["regrediu"]
// a PRIMEIRA medida nunca reprova: sem base nao ha regressao
nova := P.conferir("nunca-medida", {"p95": 1.0}, {"arquivo": base})
assert not nova["conhecida"] and not nova["regrediu"]
IO.delete(base)