Această practică implică crearea de teste automate care măsoară performanța codului considerat critic (de exemplu, algoritmi complecși sau endpoint-uri API des folosite) și integrarea lor în pipeline-ul de integrare continuă (CI). Este esențială pentru a preveni regresiile de performanță care pot scădea treptat viteza aplicației, afectând experiența utilizatorilor și costurile de infrastructură. Dacă ignori acest aspect, riscă să introdugi accidental cod ineficient în producție, descoperind problema abia când utilizatorii se plâng de latență ridicată.
Cum o aplici
- Identifică codul critic: Analizează profilarea existentă sau datele de producție pentru a găsi funcțiile care consumă cel mai mult timp CPU sau memorie.
- Scrie benchmark-uri unitare: Folosește framework-ul de benchmarking al limbajului tău pentru a măsura timpul de execuție sau allocările de memorie ale funcțiilor identificate.
- Stabilește praguri (thresholds): Definește o valoare maximă acceptabilă pentru timp sau memorie, bazată pe versiunea curentă stabilă.
- Integrează în CI: Adaugă rularea acestor benchmark-uri în pipeline-ul tău de CI (ex: GitHub Actions, GitLab CI), asigurându-te că testul eșuează dacă performanța scade sub pragul stabilit.
Exemplu
Să presupunem că ai o funcție de sortare în Python.
Varianta greșită (test funcțional simplu):
def test_sort():
assert sort([3, 1, 2]) == [1, 2, 3]
Acest test verifică corectitudinea, dar nu detectează dacă ai înlocuit algoritmul O(n log n) cu unul O(n^2).
Varianta corectă (benchmark cu pytest-benchmark):
def test_sort_performance(benchmark):
data = list(range(1000))
# Benchmark-ul va rula funcția de multe ori și va calcula media
result = benchmark(sort, data)
assert result == sorted(data)
În configurarea CI, poți seta un prag: dacă timpul mediu crește cu peste 10% față de branch-ul principal, build-ul eșuează.
Cum verifici
Verifici aplicarea prin rapoartele generate de tool-ul de benchmarking în jurnalul de build al CI. Caută metrici precum mean time, stddev și compară-le cu valoarea de referință (baseline). Unele framework-uri permit generarea de grafice istorice pentru a vizualiza tendințele pe termen lung. De asemenea, poți verifica dacă testul este marcat ca critical sau blocker în pipeline, astfel încât orice degradare semnificativă să blocheze merge-ul codului.
Greșeli frecvente
- Benchmark-uri instabile: Rularea pe mașini virtuale cu resurse partajate poate duce la rezultate zgomotoase. Asigură-te că mediul CI este izolat și dedicat.
- Ignorarea overhead-ului: Nu include setup-ul datelor în măsurătoare dacă acesta nu face parte din logica critică, sau viceversa, în funcție de ce vrei să optimizezi.
- Praguri prea stricte: Setarea unor limite imposibil de menținut duce la „alert fatigue” și la dezactivarea testelor. Ajustează pragurile periodic pe baza evoluției hardware-ului și a optimizărilor reale.
Alte subiecte din „Măsurare și profilare”
- Definește obiective de latență pe percentile (p50, p95, p99)
- Monitorizează aplicația cu un APM (Datadog, New Relic, Elastic APM)
- Instrumentează serviciile cu distributed tracing (OpenTelemetry)
- Profilează CPU-ul și memoria înainte de a optimiza
- Analizează hot path-urile cu flame graphs
- Activează continuous profiling în producție (Pyroscope, Parca)
- Măsoară impactul fiecărei optimizări, nu presupune