Los tres modos
Un fuente .vx no se ata a una forma de ejecutarse. El mismo fichero, sin cambiar una línea, puede correr en una máquina virtual, compilarse mientras se ejecuta, o convertirse en un ejecutable nativo que no necesita nada instalado.
Este es el programa que vamos a usar:
// El mismo fuente corre en la maquina virtual, se compila en caliente con el
// JIT o se convierte en un ejecutable nativo. No cambia una linea.
i64 fib(i64 n) {
if (n < 2) { return n; }
i64 a = 0;
i64 b = 1;
i64 i = 2;
while (i <= n) {
i64 next = a + b;
a = b;
b = next;
i = i + 1;
}
return b;
}
i32 main() {
println("fib(40) = ${fib(40)}");
return 0;
}
En la máquina virtual
El compilador produce bytecode y la máquina virtual lo ejecuta:
vesta --vx modes.vx -o modes
vesta --run modes.velbArranca al instante y el .velb es portable: el mismo fichero corre en Windows y en Linux sin recompilar.
Con el JIT
Aquí viene lo primero que sorprende: el JIT ya estaba funcionando. La orden de arriba no ejecuta un intérprete puro. Por defecto, la máquina virtual compila a código máquina cualquier función que se invoque más de 1500 veces, y sigue interpretando el resto.
Para verlo:
vesta --run modes.velb --jit-statsSi quieres que compile en cuanto una función se llama por primera vez, en lugar de esperar a que se caliente:
vesta --run modes.velb -m jitEso baja el umbral a 1. Es útil para comprobar que algo se compila; en un programa real no interesa, porque se gasta tiempo compilando código que apenas se ejecuta. El umbral también se fija a mano con --jit-threshold N.
Y al revés, para ejecutar sin JIT, con el intérprete a secas:
vesta --run modes.velb -m vmLa diferencia se nota. Un fib(27) recursivo, en el equipo donde se escribió esta página, tarda unos 100 ms con -m vm y unos 66 ms por defecto.
No hay dos semánticas distintas: el JIT y el intérprete producen el mismo resultado, y si el JIT se encuentra una operación que aún no sabe compilar, esa parte vuelve al intérprete sin que el programa lo note.
Compilado a nativo
El tercer modo no usa la máquina virtual en absoluto:
vesta --vx modes.vx -m aot -o modes
./modesEso produce un ejecutable del sistema (PE en Windows, ELF en Linux) que no necesita Vesta instalado. Puedes copiarlo a otra máquina y funciona.
Y no lleva runtime dentro salvo que lo pidas: la memoria se libera de forma determinista al salir de ámbito, sin recolector. Solo aparece uno si usas gc<T>, y entonces se enlaza dentro del propio binario.
Cuál usar
| Modo | Orden | Cuándo |
|---|---|---|
| Intérprete puro | -m vm | Diagnóstico, o comparar contra el JIT |
| Intérprete y JIT | (por defecto) | Desarrollo y uso normal |
| JIT agresivo | -m jit | Comprobar que algo se compila |
| Nativo | -m aot | Distribuir, arrancar rápido, sistemas sin runtime |
Lo importante no es la tabla, sino que la elección no condiciona cómo escribes. En la mayoría de lenguajes, decidir entre una máquina virtual y un binario nativo es decidir el lenguaje. Aquí es una opción de la línea de órdenes.
Por qué comparten resultado
Los tres caminos salen del mismo IR. El JIT y el compilador nativo usan además el mismo optimizador y el mismo asignador de registros, así que no hay dos implementaciones que puedan divergir con el tiempo.
En Arquitectura está el detalle de ese recorrido.