{"schemaVersion":1,"id":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques","title":"Técnicas de Compilación JIT: Cranelift, Copy-and-Patch y CPython 3.13","description":"Análisis de técnicas modernas de compilación JIT, el enfoque de Cranelift para generación rápida de código, copy-and-patch, estrategias de compilación por niveles, asignación de registros y la arquitectura interna del compilador JIT experimental de CPython 3.13.","language":"es","datePublished":"2025-11-12","dateModified":null,"contentFormat":"text/markdown","content":"Compilers\n\n# Técnicas de Compilación JIT: Cranelift, Copy-and-Patch y CPython 3.13\n\n2025-11-125 min lectura\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nTambién disponible en: [English](<https://lucioduran.com/blog/jit-compilation-cranelift-copy-patch-techniques>)\n\n## Por Qué la Compilación JIT Es Difícil\n\nLa tensión fundamental en compilación JIT: la compilación pasa en runtime, así que el tiempo de compilación es latencia visible para el usuario. Un JIT que tarda 500ms en compilar una función produce código máquina perfecto pero hace esperar al usuario. Uno que tarda 1ms produce código adecuado inmediatamente.\n\nEste tradeoff se manifiesta en cada decisión de diseño:\n\n- **Register allocation**: Óptimo (graph coloring) tarda O(n^2+), linear scan tarda O(n), y copy-and-patch lo evita completamente\n- **Instruction selection**: Tiling óptimo es NP-hard, selección greedy es O(n), stencils son O(1) por instrucción\n- **Optimización**: LLVM corre 50+ passes, Cranelift corre ~10, copy-and-patch corre cero\n\n## Cranelift: el punto intermedio\n\n[Cranelift](<https://cranelift.dev/>) es el generador de código de Bytecode Alliance, diseñado específicamente para casos de uso JIT. Alimenta [Wasmtime](<https://wasmtime.dev/>) y se está integrando en Firefox para compilación WebAssembly.\n\n### El IR CLIF\n\n```\nfunction %multiply_accumulate(i64, i64, i64) -> i64 {\nblock0(v0: i64, v1: i64, v2: i64):\n v3 = imul v0, v1\n v4 = iadd v3, v2\n return v4\n}\n```\n\nSi se está armando un runtime de lenguaje, generars CLIF desde tu bytecode:\n\n```rust\nuse cranelift::prelude::*;\n\nfn compile_function(\n module: &mut impl Module,\n bytecode: &[Opcode],\n) -> Result<*const u8, CompileError> {\n let mut ctx = module.make_context();\n let mut builder_ctx = FunctionBuilderContext::new();\n\n let mut sig = module.make_signature();\n sig.params.push(AbiParam::new(types::I64));\n sig.returns.push(AbiParam::new(types::I64));\n ctx.func.signature = sig;\n\n let mut builder = FunctionBuilder::new(&mut ctx.func, &mut builder_ctx);\n let entry_block = builder.create_block();\n builder.append_block_params_for_function_params(entry_block);\n builder.switch_to_block(entry_block);\n\n let mut value_stack: Vec<Value> = Vec::new();\n\n for op in bytecode {\n match op {\n Opcode::LoadConst(n) => {\n let val = builder.ins().iconst(types::I64, *n as i64);\n value_stack.push(val);\n }\n Opcode::Add => {\n let b = value_stack.pop().unwrap();\n let a = value_stack.pop().unwrap();\n value_stack.push(builder.ins().iadd(a, b));\n }\n Opcode::Mul => {\n let b = value_stack.pop().unwrap();\n let a = value_stack.pop().unwrap();\n value_stack.push(builder.ins().imul(a, b));\n }\n Opcode::Return => {\n let result = value_stack.pop().unwrap();\n builder.ins().return_(&[result]);\n }\n }\n }\n\n builder.seal_all_blocks();\n builder.finalize();\n\n let func_id = module.declare_function(\"compiled_fn\", Linkage::Local, &ctx.func.signature)?;\n module.define_function(func_id, &mut ctx)?;\n module.finalize_definitions()?;\n Ok(module.get_finalized_function(func_id))\n}\n```\n\n### Optimización con E-graphs\n\nEl pass de optimización de Cranelift usa e-graphs (equality graphs), que es novedoso para un compilador de producción. Los optimizadores tradicionales aplican reglas de reescritura en un orden fijo. Los e-graphs representan múltiples formas equivalentes de una expresión simultáneamente, evitando el problema de phase-ordering.\n\n### Register Allocation: regalloc2\n\n| Allocator | Tiempo | Calidad de Código |\n| --- | --- | --- |\n| regalloc2 (ion) | 0.8ms | 92% del óptimo |\n| Linear scan | 0.4ms | 85% del óptimo |\n| Graph coloring | 12ms | 99% del óptimo |\n| LLVM greedy | 45ms | 97% del óptimo |\n\n## Copy-and-Patch: Compilación con Zero Overhead\n\nCopy-and-patch es la filosofía opuesta. En vez de compilar código en runtime, pre-compilás templates (stencils) ahead of time y simplemente los copiás en su lugar.\n\nLa \"compilación\" JIT es entonces trivial:\n\n```c\nvoid* jit_compile(Opcode* bytecode, int len) {\n void* buffer = mmap(NULL, len * MAX_STENCIL_SIZE,\n PROT_READ | PROT_WRITE | PROT_EXEC,\n MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n char* cursor = (char*)buffer;\n\n for (int i = 0; i < len; i++) {\n Stencil* s = &stencils[bytecode[i].opcode];\n memcpy(cursor, s->code, s->size);\n\n for (int j = 0; j < s->num_holes; j++) {\n Hole* h = &s->holes[j];\n uint64_t value;\n switch (h->kind) {\n case HOLE_CONST: value = bytecode[i].operand; break;\n case HOLE_NEXT: value = (uint64_t)(cursor + s->size); break;\n case HOLE_STACK_PTR: value = (uint64_t)&runtime_stack_ptr; break;\n }\n memcpy(cursor + h->offset, &value, sizeof(value));\n }\n cursor += s->size;\n }\n return buffer;\n}\n```\n\nTiempo de compilación: ~10 nanosegundos por instrucción bytecode. Tres órdenes de magnitud más rápido que Cranelift. El tradeoff: el código generado es peor porque cada stencil se compila en aislamiento.\n\n## Cómo Funciona el JIT de CPython 3.13\n\nCPython 3.13 introdujo un optimizador \"tier 2\" que trabaja sobre el intérprete de bytecode existente:\n\n```\nTier 0: Intérprete de bytecode CPython (existente)\n ↓ (después de threshold de hotness)\nTier 1: Bytecode especializado (intérprete adaptivo)\n ↓ (después de más profiling)\nTier 2: Trazas JIT-compiladas (copy-and-patch)\n```\n\nEl JIT no compila funciones enteras. Compila **trazas**, secuencias lineales de bytecodes especializados que representan caminos calientes a través del código.\n\n## Comparación de Performance\n\nLos siguientes benchmarks comparan los tres approaches en un workload de modelado financiero (simulación Monte Carlo, 10M iteraciones de un modelo bytecoded de 200 instrucciones):\n\n| Approach | Tiempo Compilación | Tiempo Ejecución | Total |\n| --- | --- | --- | --- |\n| Solo intérprete | 0ms | 34,200ms | 34,200ms |\n| Copy-and-patch | 0.02ms | 8,400ms | 8,400ms |\n| Cranelift | 12ms | 4,800ms | 4,812ms |\n| LLVM -O2 | 890ms | 3,600ms | 4,490ms |\n\nPara simulaciones de larga duración, Cranelift gana. Los 12ms de compile time son invisibles cuando la simulación corre por segundos, y el código generado es 43% más rápido que copy-and-patch.\n\n## Consideraciones Prácticas de Implementación\n\nUn JIT basado en Cranelift representativo para modelado financiero podría involucrar:\n\n- ~2,800 líneas de Rust para el compilador bytecode-a-CLIF\n- ~400 líneas para el runtime (memory management, FFI al host)\n- Tiempo de compilación: 5-15ms para modelos típicos\n- Speedup de ejecución: 4-8x sobre el intérprete\n\nEl insight clave no es cuál approach JIT es \"mejor\", es entender el workload lo suficientemente bien como para elegir el punto correcto en la curva de tiempo-de-compilación vs. calidad-de-código. Para código de vida corta, compilación rápida gana. Para código de vida larga, calidad de código gana. Para código de vida media, compilación tiered da ambos, pero al costo de significativamente más complejidad de ingeniería.\n\nPara un runtime de lenguaje de propósito general, arrancar con un JIT copy-and-patch (genuinamente simple de implementar con un intérprete de bytecode existente) y agregar Cranelift como compilador tier 2 solo cuando el profiling muestre que la calidad de código es el bottleneck es un approach pragmático.\n\n## Preguntas frecuentes\n\n### ¿Qué es la compilación copy-and-patch?\n\nCopy-and-patch es una técnica JIT donde se precompilan templates de código (stencils) para cada instrucción bytecode usando un compilador tradicional como Clang/LLVM, y luego en runtime se copian estos stencils precompilados a un buffer y se parchean los operandos (constantes, direcciones, mapeos de registros). Es extremadamente rápido de compilar (~10ns por bytecode) porque no hay optimización ni asignación de registros en runtime.\n\n### ¿Cómo se compara Cranelift con LLVM para compilación JIT?\n\nCranelift compila 5-10x más rápido que LLVM produciendo código típicamente 10-30% más lento. Para case es de uso JIT donde la latencia de compilación importa (WebAssembly, runtimes de lenguajes), este tradeoff generalmente vale la pena. LLVM sigue siendo mejor para compilación ahead-of-time donde el tiempo de compilación no afecta a los usuarios.\n\n### ¿El JIT de CPython está listo para producción?\n\nA CPython 3.13, el JIT es experimental y deshabilitado por defecto. Se habilita con --enable-experimental-jit al compilar. Proporciona mejoras de rendimiento modestas (5-15% en benchmarks) y es primariamente una base para trabajo de optimización futuro. Todavía no se recomienda para cargas de trabajo de producción.\n\n## Tips\n\n1. **Tip:** El pase de optimización basado en ` egraph ` de Cranelift es donde ocurre la mayor parte de la optimización. Al construir un runtime de lenguaje, conviene invertir tiempo en comprender los e-graphs, permiten explorar el espacio de optimización sin comprometerse a un orden de reescritura específico, lo que evita el problema de phase-ordering que afecta a compiladores tradicionales.\n2. **Tip:** Los JITs copy-and-patch funcionan mejor cuando el conjunto de instrucciones es reducido y predecible (como intérpretes de bytecode). Si el IR tiene más de ~200 operaciones distintas, la biblioteca de stencils se vuelve inmanejable y conviene un JIT tradicional como Cranelift.\n3. **Tip:** Al perfilar el overhead de compilación JIT, medir ` time-to-first-execution ` por separado de ` peak-throughput `. Un JIT rápido que produce código mediocre (como copy-and-patch) supera a un JIT lento que produce código excelente (como LLVM) para cargas de trabajo de vida corta. Por ello existe la compilación por niveles.\n\n- jit\n- cranelift\n- copy-and-patch\n- cpython\n- compiladores\n- register-allocation\n- compilacion-tiered\n- wasm\n\n[Volver al Blog](<https://lucioduran.com/es/blog>)","headings":[{"level":1,"text":"Técnicas de Compilación JIT: Cranelift, Copy-and-Patch y CPython 3.13","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#title"},{"level":2,"text":"Por Qué la Compilación JIT Es Difícil","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#por-qu-la-compilacin-jit-es-difcil"},{"level":2,"text":"Cranelift: el punto intermedio","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#cranelift-el-punto-intermedio"},{"level":3,"text":"El IR CLIF","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#el-ir-clif"},{"level":3,"text":"Optimización con E-graphs","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#optimizacin-con-e-graphs"},{"level":3,"text":"Register Allocation: regalloc2","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#register-allocation-regalloc2"},{"level":2,"text":"Copy-and-Patch: Compilación con Zero Overhead","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#copy-and-patch-compilacin-con-zero-overhead"},{"level":2,"text":"Cómo Funciona el JIT de CPython 3.13","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#cmo-funciona-el-jit-de-cpython-313"},{"level":2,"text":"Comparación de Performance","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#comparacin-de-performance"},{"level":2,"text":"Consideraciones Prácticas de Implementación","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#consideraciones-prcticas-de-implementacin"},{"level":2,"text":"Preguntas frecuentes","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#questions"},{"level":3,"text":"¿Qué es la compilación copy-and-patch?","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#question-1"},{"level":3,"text":"¿Cómo se compara Cranelift con LLVM para compilación JIT?","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#question-2"},{"level":3,"text":"¿El JIT de CPython está listo para producción?","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#question-3"},{"level":2,"text":"Tips","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#article-tips"}],"sections":[{"heading":"Técnicas de Compilación JIT: Cranelift, Copy-and-Patch y CPython 3.13","level":1,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques","content":"Compilers"},{"heading":"Técnicas de Compilación JIT: Cranelift, Copy-and-Patch y CPython 3.13","level":1,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#title","content":"2025-11-125 min lectura\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nTambién disponible en: [English](<https://lucioduran.com/blog/jit-compilation-cranelift-copy-patch-techniques>)"},{"heading":"Por Qué la Compilación JIT Es Difícil","level":2,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#por-qu-la-compilacin-jit-es-difcil","content":"La tensión fundamental en compilación JIT: la compilación pasa en runtime, así que el tiempo de compilación es latencia visible para el usuario. Un JIT que tarda 500ms en compilar una función produce código máquina perfecto pero hace esperar al usuario. Uno que tarda 1ms produce código adecuado inmediatamente.\n\nEste tradeoff se manifiesta en cada decisión de diseño:\n\n- **Register allocation**: Óptimo (graph coloring) tarda O(n^2+), linear scan tarda O(n), y copy-and-patch lo evita completamente\n- **Instruction selection**: Tiling óptimo es NP-hard, selección greedy es O(n), stencils son O(1) por instrucción\n- **Optimización**: LLVM corre 50+ passes, Cranelift corre ~10, copy-and-patch corre cero"},{"heading":"Cranelift: el punto intermedio","level":2,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#cranelift-el-punto-intermedio","content":"[Cranelift](<https://cranelift.dev/>) es el generador de código de Bytecode Alliance, diseñado específicamente para casos de uso JIT. Alimenta [Wasmtime](<https://wasmtime.dev/>) y se está integrando en Firefox para compilación WebAssembly."},{"heading":"El IR CLIF","level":3,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#el-ir-clif","content":"```\nfunction %multiply_accumulate(i64, i64, i64) -> i64 {\nblock0(v0: i64, v1: i64, v2: i64):\n v3 = imul v0, v1\n v4 = iadd v3, v2\n return v4\n}\n```\n\nSi se está armando un runtime de lenguaje, generars CLIF desde tu bytecode:\n\n```rust\nuse cranelift::prelude::*;\n\nfn compile_function(\n module: &mut impl Module,\n bytecode: &[Opcode],\n) -> Result<*const u8, CompileError> {\n let mut ctx = module.make_context();\n let mut builder_ctx = FunctionBuilderContext::new();\n\n let mut sig = module.make_signature();\n sig.params.push(AbiParam::new(types::I64));\n sig.returns.push(AbiParam::new(types::I64));\n ctx.func.signature = sig;\n\n let mut builder = FunctionBuilder::new(&mut ctx.func, &mut builder_ctx);\n let entry_block = builder.create_block();\n builder.append_block_params_for_function_params(entry_block);\n builder.switch_to_block(entry_block);\n\n let mut value_stack: Vec<Value> = Vec::new();\n\n for op in bytecode {\n match op {\n Opcode::LoadConst(n) => {\n let val = builder.ins().iconst(types::I64, *n as i64);\n value_stack.push(val);\n }\n Opcode::Add => {\n let b = value_stack.pop().unwrap();\n let a = value_stack.pop().unwrap();\n value_stack.push(builder.ins().iadd(a, b));\n }\n Opcode::Mul => {\n let b = value_stack.pop().unwrap();\n let a = value_stack.pop().unwrap();\n value_stack.push(builder.ins().imul(a, b));\n }\n Opcode::Return => {\n let result = value_stack.pop().unwrap();\n builder.ins().return_(&[result]);\n }\n }\n }\n\n builder.seal_all_blocks();\n builder.finalize();\n\n let func_id = module.declare_function(\"compiled_fn\", Linkage::Local, &ctx.func.signature)?;\n module.define_function(func_id, &mut ctx)?;\n module.finalize_definitions()?;\n Ok(module.get_finalized_function(func_id))\n}\n```"},{"heading":"Optimización con E-graphs","level":3,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#optimizacin-con-e-graphs","content":"El pass de optimización de Cranelift usa e-graphs (equality graphs), que es novedoso para un compilador de producción. Los optimizadores tradicionales aplican reglas de reescritura en un orden fijo. Los e-graphs representan múltiples formas equivalentes de una expresión simultáneamente, evitando el problema de phase-ordering."},{"heading":"Register Allocation: regalloc2","level":3,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#register-allocation-regalloc2","content":"| Allocator | Tiempo | Calidad de Código |\n| --- | --- | --- |\n| regalloc2 (ion) | 0.8ms | 92% del óptimo |\n| Linear scan | 0.4ms | 85% del óptimo |\n| Graph coloring | 12ms | 99% del óptimo |\n| LLVM greedy | 45ms | 97% del óptimo |"},{"heading":"Copy-and-Patch: Compilación con Zero Overhead","level":2,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#copy-and-patch-compilacin-con-zero-overhead","content":"Copy-and-patch es la filosofía opuesta. En vez de compilar código en runtime, pre-compilás templates (stencils) ahead of time y simplemente los copiás en su lugar.\n\nLa \"compilación\" JIT es entonces trivial:\n\n```c\nvoid* jit_compile(Opcode* bytecode, int len) {\n void* buffer = mmap(NULL, len * MAX_STENCIL_SIZE,\n PROT_READ | PROT_WRITE | PROT_EXEC,\n MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n char* cursor = (char*)buffer;\n\n for (int i = 0; i < len; i++) {\n Stencil* s = &stencils[bytecode[i].opcode];\n memcpy(cursor, s->code, s->size);\n\n for (int j = 0; j < s->num_holes; j++) {\n Hole* h = &s->holes[j];\n uint64_t value;\n switch (h->kind) {\n case HOLE_CONST: value = bytecode[i].operand; break;\n case HOLE_NEXT: value = (uint64_t)(cursor + s->size); break;\n case HOLE_STACK_PTR: value = (uint64_t)&runtime_stack_ptr; break;\n }\n memcpy(cursor + h->offset, &value, sizeof(value));\n }\n cursor += s->size;\n }\n return buffer;\n}\n```\n\nTiempo de compilación: ~10 nanosegundos por instrucción bytecode. Tres órdenes de magnitud más rápido que Cranelift. El tradeoff: el código generado es peor porque cada stencil se compila en aislamiento."},{"heading":"Cómo Funciona el JIT de CPython 3.13","level":2,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#cmo-funciona-el-jit-de-cpython-313","content":"CPython 3.13 introdujo un optimizador \"tier 2\" que trabaja sobre el intérprete de bytecode existente:\n\n```\nTier 0: Intérprete de bytecode CPython (existente)\n ↓ (después de threshold de hotness)\nTier 1: Bytecode especializado (intérprete adaptivo)\n ↓ (después de más profiling)\nTier 2: Trazas JIT-compiladas (copy-and-patch)\n```\n\nEl JIT no compila funciones enteras. Compila **trazas**, secuencias lineales de bytecodes especializados que representan caminos calientes a través del código."},{"heading":"Comparación de Performance","level":2,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#comparacin-de-performance","content":"Los siguientes benchmarks comparan los tres approaches en un workload de modelado financiero (simulación Monte Carlo, 10M iteraciones de un modelo bytecoded de 200 instrucciones):\n\n| Approach | Tiempo Compilación | Tiempo Ejecución | Total |\n| --- | --- | --- | --- |\n| Solo intérprete | 0ms | 34,200ms | 34,200ms |\n| Copy-and-patch | 0.02ms | 8,400ms | 8,400ms |\n| Cranelift | 12ms | 4,800ms | 4,812ms |\n| LLVM -O2 | 890ms | 3,600ms | 4,490ms |\n\nPara simulaciones de larga duración, Cranelift gana. Los 12ms de compile time son invisibles cuando la simulación corre por segundos, y el código generado es 43% más rápido que copy-and-patch."},{"heading":"Consideraciones Prácticas de Implementación","level":2,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#consideraciones-prcticas-de-implementacin","content":"Un JIT basado en Cranelift representativo para modelado financiero podría involucrar:\n\n- ~2,800 líneas de Rust para el compilador bytecode-a-CLIF\n- ~400 líneas para el runtime (memory management, FFI al host)\n- Tiempo de compilación: 5-15ms para modelos típicos\n- Speedup de ejecución: 4-8x sobre el intérprete\n\nEl insight clave no es cuál approach JIT es \"mejor\", es entender el workload lo suficientemente bien como para elegir el punto correcto en la curva de tiempo-de-compilación vs. calidad-de-código. Para código de vida corta, compilación rápida gana. Para código de vida larga, calidad de código gana. Para código de vida media, compilación tiered da ambos, pero al costo de significativamente más complejidad de ingeniería.\n\nPara un runtime de lenguaje de propósito general, arrancar con un JIT copy-and-patch (genuinamente simple de implementar con un intérprete de bytecode existente) y agregar Cranelift como compilador tier 2 solo cuando el profiling muestre que la calidad de código es el bottleneck es un approach pragmático."},{"heading":"Preguntas frecuentes","level":2,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#questions","content":""},{"heading":"¿Qué es la compilación copy-and-patch?","level":3,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#question-1","content":"Copy-and-patch es una técnica JIT donde se precompilan templates de código (stencils) para cada instrucción bytecode usando un compilador tradicional como Clang/LLVM, y luego en runtime se copian estos stencils precompilados a un buffer y se parchean los operandos (constantes, direcciones, mapeos de registros). Es extremadamente rápido de compilar (~10ns por bytecode) porque no hay optimización ni asignación de registros en runtime."},{"heading":"¿Cómo se compara Cranelift con LLVM para compilación JIT?","level":3,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#question-2","content":"Cranelift compila 5-10x más rápido que LLVM produciendo código típicamente 10-30% más lento. Para case es de uso JIT donde la latencia de compilación importa (WebAssembly, runtimes de lenguajes), este tradeoff generalmente vale la pena. LLVM sigue siendo mejor para compilación ahead-of-time donde el tiempo de compilación no afecta a los usuarios."},{"heading":"¿El JIT de CPython está listo para producción?","level":3,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#question-3","content":"A CPython 3.13, el JIT es experimental y deshabilitado por defecto. Se habilita con --enable-experimental-jit al compilar. Proporciona mejoras de rendimiento modestas (5-15% en benchmarks) y es primariamente una base para trabajo de optimización futuro. Todavía no se recomienda para cargas de trabajo de producción."},{"heading":"Tips","level":2,"url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#article-tips","content":"1. **Tip:** El pase de optimización basado en ` egraph ` de Cranelift es donde ocurre la mayor parte de la optimización. Al construir un runtime de lenguaje, conviene invertir tiempo en comprender los e-graphs, permiten explorar el espacio de optimización sin comprometerse a un orden de reescritura específico, lo que evita el problema de phase-ordering que afecta a compiladores tradicionales.\n2. **Tip:** Los JITs copy-and-patch funcionan mejor cuando el conjunto de instrucciones es reducido y predecible (como intérpretes de bytecode). Si el IR tiene más de ~200 operaciones distintas, la biblioteca de stencils se vuelve inmanejable y conviene un JIT tradicional como Cranelift.\n3. **Tip:** Al perfilar el overhead de compilación JIT, medir ` time-to-first-execution ` por separado de ` peak-throughput `. Un JIT rápido que produce código mediocre (como copy-and-patch) supera a un JIT lento que produce código excelente (como LLVM) para cargas de trabajo de vida corta. Por ello existe la compilación por niveles.\n\n- jit\n- cranelift\n- copy-and-patch\n- cpython\n- compiladores\n- register-allocation\n- compilacion-tiered\n- wasm\n\n[Volver al Blog](<https://lucioduran.com/es/blog>)"}],"links":[{"text":"English","url":"https://lucioduran.com/blog/jit-compilation-cranelift-copy-patch-techniques"},{"text":"Cranelift","url":"https://cranelift.dev/"},{"text":"Wasmtime","url":"https://wasmtime.dev/"},{"text":"Volver al Blog","url":"https://lucioduran.com/es/blog"}],"structuredData":[{"@context":"https://schema.org","@graph":[{"@type":"BlogPosting","@id":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques#article","mainEntityOfPage":{"@type":"WebPage","@id":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques"},"headline":"Técnicas de Compilación JIT: Cranelift, Copy-and-Patch y CPython 3.13","description":"Análisis de técnicas modernas de compilación JIT, el enfoque de Cranelift para generación rápida de código, copy-and-patch, estrategias de compilación por niveles, asignación de registros y la arquitectura interna del compilador JIT experimental de CPython 3.13.","datePublished":"2025-11-12","author":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"publisher":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"image":"https://lucioduran.com/favicons/og-default.png","url":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques","inLanguage":"es","isPartOf":{"@id":"https://lucioduran.com/blog#blog"},"articleSection":"Compilers","keywords":"jit, cranelift, copy-and-patch, cpython, compiladores, register-allocation, compilacion-tiered, wasm","wordCount":865,"timeRequired":"PT5M","workTranslation":[{"@type":"BlogPosting","inLanguage":"en","url":"https://lucioduran.com/blog/jit-compilation-cranelift-copy-patch-techniques"}]},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Inicio","item":"https://lucioduran.com/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://lucioduran.com/es/blog"},{"@type":"ListItem","position":3,"name":"Técnicas de Compilación JIT: Cranelift, Copy-and-Patch y CPython 3.13","item":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques"}]},{"@type":"FAQPage","mainEntity":[{"@type":"Question","name":"¿Qué es la compilación copy-and-patch?","acceptedAnswer":{"@type":"Answer","text":"Copy-and-patch es una técnica JIT donde se precompilan templates de código (stencils) para cada instrucción bytecode usando un compilador tradicional como Clang/LLVM, y luego en runtime se copian estos stencils precompilados a un buffer y se parchean los operandos (constantes, direcciones, mapeos de registros). Es extremadamente rápido de compilar (~10ns por bytecode) porque no hay optimización ni asignación de registros en runtime."}},{"@type":"Question","name":"¿Cómo se compara Cranelift con LLVM para compilación JIT?","acceptedAnswer":{"@type":"Answer","text":"Cranelift compila 5-10x más rápido que LLVM produciendo código típicamente 10-30% más lento. Para case es de uso JIT donde la latencia de compilación importa (WebAssembly, runtimes de lenguajes), este tradeoff generalmente vale la pena. LLVM sigue siendo mejor para compilación ahead-of-time donde el tiempo de compilación no afecta a los usuarios."}},{"@type":"Question","name":"¿El JIT de CPython está listo para producción?","acceptedAnswer":{"@type":"Answer","text":"A CPython 3.13, el JIT es experimental y deshabilitado por defecto. Se habilita con --enable-experimental-jit al compilar. Proporciona mejoras de rendimiento modestas (5-15% en benchmarks) y es primariamente una base para trabajo de optimización futuro. Todavía no se recomienda para cargas de trabajo de producción."}}]}]}],"contentHash":"c8d068b2b3f25bead72d33b6a3cb1974b1d65c9ed20ba2961a478c70d01c1a82","provenance":{"method":"Deterministic serialization of the server-rendered main element","source":"https://lucioduran.com/es/blog/jit-compilation-cranelift-copy-patch-techniques","hashAlgorithm":"sha256","hashScope":"JSON document fields before contentHash and provenance","sourceHTMLBytes":58199,"contentBytes":9735}}
