Profile-Guided Optimization (PGO)
Archivo: src/pgo.rs (387 líneas)
¿Por qué existe?
Recolecta perfiles de ejecución reales y recompila guiada por perfil para optimizar las rutas que el programa realmente ejecuta. En lugar de optimizar a ciegas, PGO identifica funciones calientes, branches con patrón claro y loops con muchas iteraciones, y conduce decisiones como inlining, layout lineal de branches, unrolling agresivo y optimización tier-2.
Flujo CLI
forja run --pgo=recoger programa.fa → genera .forjaprof
forja build --pgo=usar programa.fa → recompila con el perfil La primera corrida ejecuta instrumentada y guarda el perfil en .forjaprof (en la raíz del proyecto). Las corridas posteriores con --pgo=usar cargan el perfil existente y lo aplican sin volver a recolectar. En la VM ForjaFast, el flujo conecta ProfileManager (persistencia) → aplicar_pgo (guía la ejecución) → instrumentación → ProfileManager::save (merge del perfil nuevo con el previo).
Datos recolectados
| Campo | Descripción |
|---|---|
function_hotness | Conteo de llamadas por función (record_call) |
branch_counts | Branch taken/not-taken por block: (taken, not_taken) (record_branch) |
loop_iterations | Iteraciones totales por loop (record_loop) |
type_distribution | Distribución de tipos en inline caches (record_type_at_ic) |
hot_ips | Hotness por instruction pointer del bytecode, para pre-especialización adaptativa (record_ip) |
Estructuras clave
| Componente | Descripción |
|---|---|
ProfileData | Perfil completo, serializable a JSON (serde). serialize/deserialize usan serde_json |
BranchHotness | Clasificación de un branch: HotTaken (taken > 80%), HotNotTaken (taken < 20%) o Unpredictable |
ProfileGuidedDecisions | Decisiones derivadas del perfil: inline_candidates, hot_branches, hot_loops y tier2_candidates |
Instrumenter | Contadores por IP con sampling (sample_rate, cada N instrucciones); hot_paths y hottest_ip extraen los IPs más ejecutados |
ProfileManager | Persistencia del archivo .forjaprof en el directorio del proyecto |
Decisiones de optimización
ProfileGuidedDecisions::from_profile deriva automáticamente: funciones con >= 100 llamadas van a inline_candidates; funciones con >= 1000 llamadas a tier2_candidates (optimización agresiva); branches con patrón claro (no Unpredictable) y > 10 ejecuciones van a hot_branches (layout lineal con fall-through); loops con > 10000 iteraciones van a hot_loops (unrolling agresivo). Los métodos hot_branch_ips y hot_loop_back_edges extraen los IPs correspondientes para que la VM priorice quickening en ellos.
Cómo se integra
El JIT engine (jit_engine.rs) carga el perfil con cargar_perfil, usa pgo_loop_unroll_factor para el unrolling de loops calientes, y es_pgo_tier2_candidate para decidir optimizaciones agresivas por función. La VM aplica el perfil con aplicar_pgo antes de ejecutar y mergea los datos recolectados con los previos al finalizar (lib.rs::ejecutar_con_pgo_impl).