Ir para o conteúdo

Performance baseline em CI

Diátaxis: explanation. Para o como rodar localmente, veja docs/reference/cli.md seção bench all.

.github/workflows/ci.yml ganha um job perf-baseline que roda a suite consolidada de benchmarks (PR #36, anotae bench all) em matriz [ubuntu-latest, macos-latest, windows-latest] e publica o CSV resultante como artifact com 90 dias de retenção.

Por que rodar perf em CI

Beta privado expõe o Anotae a hardwares heterogêneos (UBSs com PCs de várias gerações). Sem dado histórico, é difícil:

  • Identificar regressões de performance entre releases.
  • Decidir se um usuário com queixa de "tá lento" tem hardware que realmente justifica fallback Argon2id.
  • Comparar runner de cada SO ao longo do tempo (GitHub muda hardware silenciosamente em pools sem aviso).

Executar anotae bench all em cada commit em main produz uma trilha auditável dessas variações.

Decisões

Roda só em main e tag pushes

Um benchmark típico leva ~1–3 minutos por SO em runner padrão. Multiplicar por cada PR adicionaria ~10 minutos a cada pipeline sem ganho de informação útil (rodadas em PRs ainda em desenvolvimento têm muito ruído de revisão).

if: |
  github.ref == 'refs/heads/main' ||
  startsWith(github.ref, 'refs/tags/v')

Matriz idêntica ao smoke E2E

[ubuntu-latest, macos-latest, windows-latest] × Python 3.12. Mesma configuração do smoke captura tanto regressão funcional (smoke) quanto regressão de performance (perf-baseline). Custo combinado é manejável.

--skip-pdf por default

render_pdf em CI headless requer truque com QGuiApplication que conflita com a fixture pytest-qt. O smoke E2E em subprocess isolado já cobre o caminho do PDF.

Trade-off: perdemos métrica de export PDF nos baselines. Aceito — quem se importa com isso pode rodar localmente com --no-skip-pdf flag adicional (não implementado nesta PR; reabrir se necessário).

Cabeçalho informativo no CSV

Acrescentamos linhas iniciais # commit_sha=..., # os=..., # ref=... antes do CSV propriamente dito. Padrão # deixa a maioria dos parsers CSV ignorar essas linhas (ou tratar como comentário). Útil para auditoria sem precisar baixar metadado do artifact separadamente.

90 dias de retenção

GitHub Actions free tier suporta até 90 dias por default. Para analisar tendência semanal/mensal, isso cobre 12+ semanas — suficiente para detectar regressão antes do histórico expirar.

Comparação automática em PR (perf-regression-pr)

PR #63 adicionou um job complementar que roda em cada PR com mudanças em anotae/, scripts/ ou pyproject.toml:

  1. Roda anotae bench all produzindo perf-current.csv.
  2. Baixa via gh CLI o artifact perf-baseline-ubuntu-latest-* mais recente do main (último run com success).
  3. Roda scripts/perf-regression.py com threshold default 20% (PR #60).
  4. Imprime no log + emite annotations ::warning:: / ::error:: visíveis no PR review.
  5. Upload do perf-current.csv como artifact perf-pr-<n>-<sha> (retention 30 dias — menor que baseline porque PRs vêm e vão).

Casos especiais:

  • Primeira execução após PR #40 mesclar: não há baseline em main ainda → job emite ::warning:: "Sem run de baseline em main — pulando comparação" e exit 0 (não bloqueia o PR).
  • PR pode pular: aplicar label skip-perf no PR para o job não rodar (útil em PRs que sabidamente regridem temporariamente — ex.: refactoring com TODO de re-otimizar).
  • Apenas Linux: comparação roda em ubuntu-latest, não em matrix. macOS/Windows continuam só no perf-baseline que roda apenas em main. Manter PR rápido — comparar 3 SOs em cada PR seria 3x mais tempo de CI sem ganho proporcional.

Como consumir os artifacts

Análise manual

# Baixe o artifact 'perf-baseline-ubuntu-latest-<sha>' do release
unzip perf-baseline-ubuntu-latest-abc123.zip
cat perf-baseline.csv

Comparativo entre commits

Não há ferramenta automatizada nesta PR — apenas dados. Análise visual recomendada via pandas ou planilha simples:

import pandas as pd
df = pd.read_csv("perf-baseline.csv", comment="#")
df.groupby(["benchmark"])["elapsed_seconds"].describe()

Para tendência ao longo do tempo, baixe artifacts de runs sequenciais e concatene.

Quando reabrir esta decisão

  • Tendência analítica vira requisito → adicionar push para um serviço de armazenamento permanente (S3, branch dedicada de artifacts) com job que comenta no PR mostrando delta vs. main.
  • Runner do GitHub fica caro/instável → rodar em runner self-hosted que controla a carga.
  • Regressão de performance vira frequente → adicionar pytest- benchmark em testes individuais para enxergar regressão sem precisar esperar main merge.