{"schemaVersion":1,"id":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks","title":"Benchmarks de Migración a Graviton4 ARM64: Datos Reales de Performance en Producción","description":"Benchmarks de producción de migrar workloads a instancias AWS Graviton4 ARM64. Diferencias de rendimiento en Node.js, Go y Rust, trampas de NEON SIMD, compiler flags que importan, y el ahorro real después de 4 meses.","language":"es","datePublished":"2025-11-22","dateModified":null,"contentFormat":"text/markdown","content":"Infrastructure\n\n# Benchmarks de Migración a Graviton4 ARM64: Datos Reales de Performance en Producción\n\n2025-11-2210 min lectura\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nTambién disponible en: [English](<https://lucioduran.com/blog/arm64-graviton4-server-migration-benchmarks>)\n\n## El Setup de Tests\n\nPara que estos benchmarks tengan sentido, los detalles del hardware importan:\n\n- **Baseline x86**: m7i.xlarge, 4 vCPUs (Intel Sapphire Rapids 4ta Gen), 16GB RAM, EBS gp3\n- **Target ARM64**: r8g.xlarge, 4 vCPUs (Graviton4, Neoverse V2), 32GB RAM, EBS gp3\n- **Región**: us-east-1\n- **OS**: Amazon Linux 2023, kernel 6.1\n- **Testing**: Cada benchmark corrió 10 veces, los números son mediana con bounds p5/p95\n\nSí, el r8g tiene más RAM. Las familias de instancias deben elegirse según las necesidades reales del workload, no por simetría de benchmarks sintéticos. En producción, se elige la instancia que le queda a la aplicación, no la que hace un gráfico de comparación justo.\n\n## Node.js (v22.x): El Punto Medio Sorprendente\n\nUn servicio Node.js representativo para benchmarking es un API gateway, Express.js, mucho parsing de JSON, validación de JWT, rate limiting, transformación de respuestas. Clásico I/O-bound con ráfagas de CPU para crypto y JSON.\n\n### Throughput HTTP (wrk, 100 conexiones concurrentes, 60s)\n\n| Métrica | m7i.xlarge (x86) | r8g.xlarge (ARM64) | Delta |\n| --- | --- | --- | --- |\n| Requests/seg | 14,230 | 15,870 | +11.5% |\n| Latencia promedio | 6.89ms | 6.21ms | -9.9% |\n| Latencia p99 | 18.4ms | 14.7ms | -20.1% |\n| Uso CPU (promedio) | 72% | 64% | -11.1% |\n\nLa mejora en p99 es el resultado más notable. El rendimiento más predecible de los cores de Graviton4 (sin variación de turbo boost) te da distribuciones de latencia más ajustadas. En una arquitectura de microservicios, ese efecto compuesto de tail latency es enorme.\n\n### Serialización JSON (payload 1MB, 10K iteraciones)\n\n```javascript\n// Benchmark: JSON.parse + transformación + JSON.stringify\nconst payload = generateLargeJSON(1_000_000); // ~1MB\nconst serialized = JSON.stringify(payload);\n\nconsole.time('json-roundtrip');\nfor (let i = 0; i < 10_000; i++) {\n const parsed = JSON.parse(serialized);\n parsed.metadata.processedAt = Date.now();\n parsed.items = parsed.items.filter(i => i.active);\n JSON.stringify(parsed);\n}\nconsole.timeEnd('json-roundtrip');\n```\n\n| Runtime | x86 | ARM64 | Delta |\n| --- | --- | --- | --- |\n| Node.js 22 | 34.2s | 31.8s | -7.0% |\n| Node.js 22 + ` --max-semi-space-size=128 ` | 28.1s | 25.4s | -9.6% |\n\nEl parser JSON de V8 se beneficia del bandwidth de memoria mejorado de Graviton4. El flag de semi-space importa más en ARM64 porque el perfil de presión del GC es diferente, el cache L2 más grande de Graviton4 (2MB por core vs 1.25MB en Sapphire Rapids) mantiene más objetos young-gen calientes.\n\n### El Gotcha de Node.js: Módulos Nativos\n\nVarias dependencias npm comunes no tienen binarios ARM64 pre-compilados:\n\n- ` sharp `, tenía binarios ARM64, funcionó perfecto\n- ` bcrypt `, tenía binarios ARM64, funcionó perfecto\n- ` cpu-features `, **sin binario ARM64**, cayó a WASM, 3x más lento\n- ` re2 `, **sin binario ARM64**, compilar desde source requería ` python3 ` y ` g++ ` en el container\n- ` canvas `, **sin binario ARM64**, requería compilación manual con deps del sistema\n\nEl fallback de ` cpu-features ` es invisible, sin warnings, sin errores, simplemente más lento en silencio. Suele detectarse cuando los benchmarks de validación JWT resultan peores de lo esperado en ARM64, lo que lleva a descubrir que está usando el fallback WASM para la detección de features que hace internamente.\n\nSiempre verificá: ` npm ls --all | xargs -I {} npm info {} cpu `\n\n## Go (1.23): Donde ARM64 Realmente Brilla\n\nGo en ARM64 es otra historia. El compilador de Go tiene soporte ARM64 de primera clase desde Go 1.16, y el runtime está fuertemente optimizado para él.\n\n### Benchmark de API HTTP (net/http, JSON CRUD, backend PostgreSQL)\n\n| Métrica | m7i.xlarge (x86) | r8g.xlarge (ARM64) | Delta |\n| --- | --- | --- | --- |\n| Requests/seg | 28,450 | 34,120 | +19.9% |\n| Latencia promedio | 3.45ms | 2.88ms | -16.5% |\n| Latencia p99 | 11.2ms | 8.1ms | -27.7% |\n| Allocs/op | 42 | 42 | 0% |\n| Pausa GC (p99) | 0.8ms | 0.6ms | -25% |\n\nVeinte por ciento más de throughput. Eso no es error de redondeo. Y las pausas del GC son más cortas porque el subsistema de memoria de Graviton4 es genuinamente mejor, mayor bandwidth, menor latencia a L2/L3.\n\n### El Flag GOARM64: Performance Gratis\n\nEste es el tip que la mayoría se pierde. Go 1.23 introdujo ` GOARM64 ` para targeting granular de features ARM64:\n\n```bash\n# Build default (conservador, corre en cualquier ARM64)\nGOOS=linux GOARCH=arm64 go build -o service ./cmd/service\n\n# Build optimizado para Graviton4 (usa features ARMv9 incluyendo SVE2)\nGOOS=linux GOARCH=arm64 GOARM64=v9.0 go build -o service ./cmd/service\n```\n\nLa diferencia en una operación compute-heavy (un motor de recomendaciones, pesado en math float64):\n\n| Build | Throughput | Latencia p50 |\n| --- | --- | --- |\n| ARM64 default | 12,340 ops/s | 4.1ms |\n| GOARM64=v9.0 | 14,780 ops/s | 3.4ms |\n\nEso es 19.8% más de throughput por una sola variable de entorno. El flag ` v9.0 ` habilita instrucciones SVE2 (Scalable Vector Extension 2), que Graviton4 soporta. Para workloads con math de punto flotante, procesamiento de strings u operaciones criptográficas, es performance gratis.\n\n### Flags del Compilador con Impacto Medible\n\nMás allá de GOARM64, estos flags mostraron diferencias medibles en ARM64:\n\n```bash\n# PGO (Profile-Guided Optimization), funciona en ARM64 desde Go 1.21\ngo build -pgo=default.pgo -o service ./cmd/service\n\n# Resultados con PGO en ARM64:\n# Sin PGO: 34,120 req/s\n# Con PGO: 37,230 req/s (+9.1%)\n```\n\nPGO en ARM64 es ligeramente más efectivo que en x86 porque el branch predictor de Graviton4 se beneficia más de los datos del profile. Recolectá un CPU profile de producción (` runtime/pprof `), guardalo como ` default.pgo ` en el directorio de tu paquete main, y recompilá. Es genuinamente performance gratis.\n\n## Rust (1.82): La Bestia en ARM64\n\nRust en Graviton4 es donde las cosas se ponen interesantes. El backend ARM64 de LLVM es excepcionalmente maduro, y Rust te da el control para explotarlo.\n\n### Benchmark de Servicio de Procesamiento de Imágenes\n\nUn servicio de procesamiento de imágenes que hace redimensionamiento y conversión de formato en tiempo real (WebP, AVIF) provee un benchmark intensivo en SIMD, bandwidth de memoria y cómputo.\n\n```rust\n// Apuntar a Graviton4 específicamente\n// .cargo/config.toml\n[target.aarch64-unknown-linux-gnu]\nrustflags = [\"-C\", \"target-cpu=neoverse-v2\"]\n```\n\n| Operación | x86 (AVX2) | ARM64 (default) | ARM64 (neoverse-v2) | Delta vs x86 |\n| --- | --- | --- | --- | --- |\n| JPEG decode (4K) | 12.3ms | 14.1ms | 11.2ms | -8.9% |\n| WebP encode | 28.7ms | 31.2ms | 24.8ms | -13.6% |\n| AVIF encode | 89.4ms | 97.1ms | 78.3ms | -12.4% |\n| Resize 4K→1080p | 8.4ms | 9.1ms | 7.2ms | -14.3% |\n\nObservar la diferencia entre ARM64 default y ` target-cpu=neoverse-v2 `. Sin el flag de target CPU, ARM64 es en realidad más lento que x86 para workloads SIMD-heavy porque el compilador genera código NEON conservador. Con el flag, usa SVE2 con vectores más amplios y el rendimiento se invierte.\n\n### NEON vs SVE2: Diferencias Clave\n\nARM64 tiene dos sets de instrucciones SIMD:\n\n- **NEON**: Vectores fijos de 128 bits, disponible en todo ARM64. Equivalente a SSE4.2.\n- **SVE2**: Vectores escalables (128-512 bits, Graviton4 usa 128 bits pero con instrucciones adicionales), disponible en ARMv9. Equivalente a AVX2/AVX-512 en capacidad, aunque no en ancho de vector crudo.\n\nSi se está usando intrinsics SIMD directamente en Rust:\n\n```rust\n// NEON (funciona en todos lados)\n#[cfg(target_arch = \"aarch64\")]\nuse std::arch::aarch64::*;\n\nunsafe fn sum_neon(data: &[f32]) -> f32 {\n let mut acc = vdupq_n_f32(0.0);\n for chunk in data.chunks_exact(4) {\n let v = vld1q_f32(chunk.as_ptr());\n acc = vaddq_f32(acc, v);\n }\n vaddvq_f32(acc)\n}\n```\n\nPara la mayoría del código Rust, no se requieren intrinsics. El compilador auto-vectoriza cuando se configura el target CPU correctamente. Sin embargo, al portar código SIMD de x86, es importante considerar que NEON y AVX tienen semánticas diferentes para algunas operaciones (en particular: no existe equivalente directo de ` _mm_shuffle_ps `, se requiere ` vqtbl1q_u8 ` o similar).\n\n## Análisis de Costos: Cuatro Meses en Producción\n\nDespués de 4 meses en producción, aquí van los números reales:\n\n### Costos de Cómputo (14 servicios, us-east-1)\n\n|  | x86 (familia m7i) | ARM64 (familia r8g) | Ahorro |\n| --- | --- | --- | --- |\n| On-Demand mensual | $4,847 | $3,878 | -20.0% |\n| Reserved 1 año mensual | $3,105 | $2,484 | -20.0% |\n| Spot mensual (promedio) | $1,939 | $1,242 | -35.9% |\n\nLos ahorros en Spot son lo destacado. [AWS](<https://aws.com/>) tiene significativamente más capacidad Spot ARM64 (menos gente usándola), así que las tasas de interrupción son más bajas y los precios más estables. En un período de 4 meses, la tasa promedio de interrupción Spot en ARM64 midió 2.1% vs 5.8% en x86.\n\n### Ahorro Total en Infraestructura\n\nCombinando la mejora de performance (que permite reducir el tamaño de algunas instancias) con el menor costo por vCPU:\n\n- **Antes (x86, mix On-Demand/Reserved)**: $3,740/mes\n- **Después (ARM64, mix Reserved/Spot)**: $2,180/mes\n- **Ahorro neto**: $1,560/mes (-41.7%)\n\nEse 41.7% no es solo la diferencia de precio ARM64, incluye rightsizing de instancias, ya que los mismos workloads necesitan menos vCPUs en Graviton4.\n\n## Checklist de Migración\n\nUna checklist condensada de migración basada en experiencia de producción:\n\n1. **Imágenes base de containers**: Cambiá a multi-arch o imágenes ` arm64v8/ ` explícitas. ` FROM node:22-slim ` ya pullea la arch correcta automáticamente.\n2. **CI/CD**: Agregar runners ARM64. GitHub Actions ya tiene ` runs-on: ubuntu-24.04-arm `. Si son self-hosted, instancias Graviton3 c7g son excelentes CI runners.\n3. **Auditoría de dependencias nativas**: Ejecutar tu suite de tests en ARM64 primero. Buscá fallbacks WASM, binarios faltantes, requerimientos de compilación.\n4. **Compiler flags**: Configurar target CPU para lenguajes compilados. ` GOARM64=v9.0 ` para Go, ` target-cpu=neoverse-v2 ` para Rust, ` -march=armv9-a ` para C/C++.\n5. **Monitoreo**: Vigilar regresiones de performance en operaciones específicas. Las operaciones crypto comúnmente resultan más lentas inicialmente por falta de configuración del engine OpenSSL para ARM64.\n6. **Rollout gradual**: Utilizar target groups con pese es en tu load balancer. Empezá con 10% de tráfico a ARM64, validá métricas, incrementá.\n\n## Limitaciones y Consideraciones de los Benchmarks\n\nTodo post de benchmarks (incluyendo este) es inherentemente engañoso porque mide workloads específicos en configuraciones específicas. Los resultados van a variar. Algunas áreas se excluyen deliberadamente porque serían engañosas:\n\n- **Workloads memory-bound**: El r8g tiene características de RAM diferentes al m7i. La comparación directa es comparar peras con manzanas.\n- **Performance pico single-threaded**: El turbo boost de x86 puede llegar a 3.6GHz+. Graviton4 corre a 3.0GHz fijos. Para ráfagas de single-thread, x86 gana.\n- **Workloads AVX-512**: Si tu código usa AVX-512 (inferencia ML, computación científica), x86 tiene una ventaja genuina. El SVE2 de Graviton4 no es un equivalente directo.\n\nEl enfoque correcto: ejecutar tu workload de producción real en ambas arquitecturas con tráfico real. Los benchmarks sintéticos te dicen dirección, no magnitud.\n\n## ¿Vale la Pena?\n\nLa reducción de costos del 41% por sí sola justifica las aproximadamente dos semanas de trabajo de migración. Pero el beneficio menos cuantificable es operacional: los cores de frecuencia fija de Graviton4 entregan performance más predecible, lo que significa que el autoscaling es más responsivo y el capacity planning es más simple.\n\nPara workloads basados en AWS, la evaluación de Graviton4 es cada vez más indispensable. El ecosistema ARM64 está lo suficientemente maduro como para que la migración sea aburrida, y en infraestructura, aburrido es exactamente el objetivo.\n\n## Preguntas frecuentes\n\n### ¿Graviton4 es siempre más rápido que instancias x86 equivalentes?\n\nNo. Para workloads single-threaded con uso intenso de AVX-512, x86 todavía puede ganar. Graviton4 se destaca en workloads orientados a throughput y multi-threaded, y ofrece mejor performance-por-dólar en casi todos los escenarios testeados.\n\n### ¿Qué tan difícil es la migración de x86 a ARM64?\n\nPara lenguajes interpretados (Node.js, Python, Ruby), es mayormente sin dolor, el runtime maneja la diferencia de arquitectura. Para lenguajes compilados, se necesita cross-compilar o compilar en ARM64. Los problemas están en las dependencias nativas y las imágenes base de containers.\n\n### ¿Qué pasa con las imágenes Docker para ARM64?\n\nLa mayoría de las imágenes Docker oficiales ya proveen manifiestos multi-arch. Para imágenes custom, utilizar Docker buildx con --platform linux/arm64. El mayor dolor de cabeza son imágenes de terceros que solo proveen builds amd64.\n\n## Tips\n\n1. **Flag crítico:** Para Go en Graviton4, compilá con ` GOARM64=v9.0 ` para desbloquear instrucciones SVE2, los builds default dejan 15-20% de performance en la mesa para workloads compute-heavy.\n2. **Trampa de Node.js:** El JIT de V8 en ARM64 no auto-vectoriza tan agresivamente como en x86. Para loops numéricos ajustados, considerá ` --experimental-wasm-simd ` o descargá a addons nativos.\n3. **Truco de costos:** Las instancias Spot de Graviton4 tienen 60-70% menos frecuencia de interrupción que las equivalentes x86, AWS tiene exceso de capacidad ARM. Utilizar estrategia de asignación ` capacity-optimized `.\n4. **Imprescindible para testing:** Siempre correr la suite completa de tests en CI ARM64 antes de migrar. Algunos paquetes npm nativos no tienen binarios ARM64 y caen silenciosamente a builds WASM más lentos.\n\n- arm64\n- graviton4\n- aws\n- benchmarks\n- performance\n- migration\n- nodejs\n- go\n- rust\n\n[Volver al Blog](<https://lucioduran.com/es/blog>)","headings":[{"level":1,"text":"Benchmarks de Migración a Graviton4 ARM64: Datos Reales de Performance en Producción","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#title"},{"level":2,"text":"El Setup de Tests","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#el-setup-de-tests"},{"level":2,"text":"Node.js (v22.x): El Punto Medio Sorprendente","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#nodejs-v22x-el-punto-medio-sorprendente"},{"level":3,"text":"Throughput HTTP (wrk, 100 conexiones concurrentes, 60s)","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#throughput-http-wrk-100-conexiones-concurrentes-60s"},{"level":3,"text":"Serialización JSON (payload 1MB, 10K iteraciones)","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#serializacin-json-payload-1mb-10k-iteraciones"},{"level":3,"text":"El Gotcha de Node.js: Módulos Nativos","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#el-gotcha-de-nodejs-mdulos-nativos"},{"level":2,"text":"Go (1.23): Donde ARM64 Realmente Brilla","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#go-123-donde-arm64-realmente-brilla"},{"level":3,"text":"Benchmark de API HTTP (net/http, JSON CRUD, backend PostgreSQL)","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#benchmark-de-api-http-nethttp-json-crud-backend-postgresql"},{"level":3,"text":"El Flag GOARM64: Performance Gratis","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#el-flag-goarm64-performance-gratis"},{"level":3,"text":"Flags del Compilador con Impacto Medible","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#flags-del-compilador-con-impacto-medible"},{"level":2,"text":"Rust (1.82): La Bestia en ARM64","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#rust-182-la-bestia-en-arm64"},{"level":3,"text":"Benchmark de Servicio de Procesamiento de Imágenes","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#benchmark-de-servicio-de-procesamiento-de-imgenes"},{"level":3,"text":"NEON vs SVE2: Diferencias Clave","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#neon-vs-sve2-diferencias-clave"},{"level":2,"text":"Análisis de Costos: Cuatro Meses en Producción","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#anlisis-de-costos-cuatro-meses-en-produccin"},{"level":3,"text":"Costos de Cómputo (14 servicios, us-east-1)","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#costos-de-cmputo-14-servicios-us-east-1"},{"level":3,"text":"Ahorro Total en Infraestructura","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#ahorro-total-en-infraestructura"},{"level":2,"text":"Checklist de Migración","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#checklist-de-migracin"},{"level":2,"text":"Limitaciones y Consideraciones de los Benchmarks","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#limitaciones-y-consideraciones-de-los-benchmarks"},{"level":2,"text":"¿Vale la Pena?","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#vale-la-pena"},{"level":2,"text":"Preguntas frecuentes","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#questions"},{"level":3,"text":"¿Graviton4 es siempre más rápido que instancias x86 equivalentes?","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#question-1"},{"level":3,"text":"¿Qué tan difícil es la migración de x86 a ARM64?","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#question-2"},{"level":3,"text":"¿Qué pasa con las imágenes Docker para ARM64?","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#question-3"},{"level":2,"text":"Tips","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#article-tips"}],"sections":[{"heading":"Benchmarks de Migración a Graviton4 ARM64: Datos Reales de Performance en Producción","level":1,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks","content":"Infrastructure"},{"heading":"Benchmarks de Migración a Graviton4 ARM64: Datos Reales de Performance en Producción","level":1,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#title","content":"2025-11-2210 min lectura\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nTambién disponible en: [English](<https://lucioduran.com/blog/arm64-graviton4-server-migration-benchmarks>)"},{"heading":"El Setup de Tests","level":2,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#el-setup-de-tests","content":"Para que estos benchmarks tengan sentido, los detalles del hardware importan:\n\n- **Baseline x86**: m7i.xlarge, 4 vCPUs (Intel Sapphire Rapids 4ta Gen), 16GB RAM, EBS gp3\n- **Target ARM64**: r8g.xlarge, 4 vCPUs (Graviton4, Neoverse V2), 32GB RAM, EBS gp3\n- **Región**: us-east-1\n- **OS**: Amazon Linux 2023, kernel 6.1\n- **Testing**: Cada benchmark corrió 10 veces, los números son mediana con bounds p5/p95\n\nSí, el r8g tiene más RAM. Las familias de instancias deben elegirse según las necesidades reales del workload, no por simetría de benchmarks sintéticos. En producción, se elige la instancia que le queda a la aplicación, no la que hace un gráfico de comparación justo."},{"heading":"Node.js (v22.x): El Punto Medio Sorprendente","level":2,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#nodejs-v22x-el-punto-medio-sorprendente","content":"Un servicio Node.js representativo para benchmarking es un API gateway, Express.js, mucho parsing de JSON, validación de JWT, rate limiting, transformación de respuestas. Clásico I/O-bound con ráfagas de CPU para crypto y JSON."},{"heading":"Throughput HTTP (wrk, 100 conexiones concurrentes, 60s)","level":3,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#throughput-http-wrk-100-conexiones-concurrentes-60s","content":"| Métrica | m7i.xlarge (x86) | r8g.xlarge (ARM64) | Delta |\n| --- | --- | --- | --- |\n| Requests/seg | 14,230 | 15,870 | +11.5% |\n| Latencia promedio | 6.89ms | 6.21ms | -9.9% |\n| Latencia p99 | 18.4ms | 14.7ms | -20.1% |\n| Uso CPU (promedio) | 72% | 64% | -11.1% |\n\nLa mejora en p99 es el resultado más notable. El rendimiento más predecible de los cores de Graviton4 (sin variación de turbo boost) te da distribuciones de latencia más ajustadas. En una arquitectura de microservicios, ese efecto compuesto de tail latency es enorme."},{"heading":"Serialización JSON (payload 1MB, 10K iteraciones)","level":3,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#serializacin-json-payload-1mb-10k-iteraciones","content":"```javascript\n// Benchmark: JSON.parse + transformación + JSON.stringify\nconst payload = generateLargeJSON(1_000_000); // ~1MB\nconst serialized = JSON.stringify(payload);\n\nconsole.time('json-roundtrip');\nfor (let i = 0; i < 10_000; i++) {\n const parsed = JSON.parse(serialized);\n parsed.metadata.processedAt = Date.now();\n parsed.items = parsed.items.filter(i => i.active);\n JSON.stringify(parsed);\n}\nconsole.timeEnd('json-roundtrip');\n```\n\n| Runtime | x86 | ARM64 | Delta |\n| --- | --- | --- | --- |\n| Node.js 22 | 34.2s | 31.8s | -7.0% |\n| Node.js 22 + ` --max-semi-space-size=128 ` | 28.1s | 25.4s | -9.6% |\n\nEl parser JSON de V8 se beneficia del bandwidth de memoria mejorado de Graviton4. El flag de semi-space importa más en ARM64 porque el perfil de presión del GC es diferente, el cache L2 más grande de Graviton4 (2MB por core vs 1.25MB en Sapphire Rapids) mantiene más objetos young-gen calientes."},{"heading":"El Gotcha de Node.js: Módulos Nativos","level":3,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#el-gotcha-de-nodejs-mdulos-nativos","content":"Varias dependencias npm comunes no tienen binarios ARM64 pre-compilados:\n\n- ` sharp `, tenía binarios ARM64, funcionó perfecto\n- ` bcrypt `, tenía binarios ARM64, funcionó perfecto\n- ` cpu-features `, **sin binario ARM64**, cayó a WASM, 3x más lento\n- ` re2 `, **sin binario ARM64**, compilar desde source requería ` python3 ` y ` g++ ` en el container\n- ` canvas `, **sin binario ARM64**, requería compilación manual con deps del sistema\n\nEl fallback de ` cpu-features ` es invisible, sin warnings, sin errores, simplemente más lento en silencio. Suele detectarse cuando los benchmarks de validación JWT resultan peores de lo esperado en ARM64, lo que lleva a descubrir que está usando el fallback WASM para la detección de features que hace internamente.\n\nSiempre verificá: ` npm ls --all | xargs -I {} npm info {} cpu `"},{"heading":"Go (1.23): Donde ARM64 Realmente Brilla","level":2,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#go-123-donde-arm64-realmente-brilla","content":"Go en ARM64 es otra historia. El compilador de Go tiene soporte ARM64 de primera clase desde Go 1.16, y el runtime está fuertemente optimizado para él."},{"heading":"Benchmark de API HTTP (net/http, JSON CRUD, backend PostgreSQL)","level":3,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#benchmark-de-api-http-nethttp-json-crud-backend-postgresql","content":"| Métrica | m7i.xlarge (x86) | r8g.xlarge (ARM64) | Delta |\n| --- | --- | --- | --- |\n| Requests/seg | 28,450 | 34,120 | +19.9% |\n| Latencia promedio | 3.45ms | 2.88ms | -16.5% |\n| Latencia p99 | 11.2ms | 8.1ms | -27.7% |\n| Allocs/op | 42 | 42 | 0% |\n| Pausa GC (p99) | 0.8ms | 0.6ms | -25% |\n\nVeinte por ciento más de throughput. Eso no es error de redondeo. Y las pausas del GC son más cortas porque el subsistema de memoria de Graviton4 es genuinamente mejor, mayor bandwidth, menor latencia a L2/L3."},{"heading":"El Flag GOARM64: Performance Gratis","level":3,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#el-flag-goarm64-performance-gratis","content":"Este es el tip que la mayoría se pierde. Go 1.23 introdujo ` GOARM64 ` para targeting granular de features ARM64:\n\n```bash\n# Build default (conservador, corre en cualquier ARM64)\nGOOS=linux GOARCH=arm64 go build -o service ./cmd/service\n\n# Build optimizado para Graviton4 (usa features ARMv9 incluyendo SVE2)\nGOOS=linux GOARCH=arm64 GOARM64=v9.0 go build -o service ./cmd/service\n```\n\nLa diferencia en una operación compute-heavy (un motor de recomendaciones, pesado en math float64):\n\n| Build | Throughput | Latencia p50 |\n| --- | --- | --- |\n| ARM64 default | 12,340 ops/s | 4.1ms |\n| GOARM64=v9.0 | 14,780 ops/s | 3.4ms |\n\nEso es 19.8% más de throughput por una sola variable de entorno. El flag ` v9.0 ` habilita instrucciones SVE2 (Scalable Vector Extension 2), que Graviton4 soporta. Para workloads con math de punto flotante, procesamiento de strings u operaciones criptográficas, es performance gratis."},{"heading":"Flags del Compilador con Impacto Medible","level":3,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#flags-del-compilador-con-impacto-medible","content":"Más allá de GOARM64, estos flags mostraron diferencias medibles en ARM64:\n\n```bash\n# PGO (Profile-Guided Optimization), funciona en ARM64 desde Go 1.21\ngo build -pgo=default.pgo -o service ./cmd/service\n\n# Resultados con PGO en ARM64:\n# Sin PGO: 34,120 req/s\n# Con PGO: 37,230 req/s (+9.1%)\n```\n\nPGO en ARM64 es ligeramente más efectivo que en x86 porque el branch predictor de Graviton4 se beneficia más de los datos del profile. Recolectá un CPU profile de producción (` runtime/pprof `), guardalo como ` default.pgo ` en el directorio de tu paquete main, y recompilá. Es genuinamente performance gratis."},{"heading":"Rust (1.82): La Bestia en ARM64","level":2,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#rust-182-la-bestia-en-arm64","content":"Rust en Graviton4 es donde las cosas se ponen interesantes. El backend ARM64 de LLVM es excepcionalmente maduro, y Rust te da el control para explotarlo."},{"heading":"Benchmark de Servicio de Procesamiento de Imágenes","level":3,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#benchmark-de-servicio-de-procesamiento-de-imgenes","content":"Un servicio de procesamiento de imágenes que hace redimensionamiento y conversión de formato en tiempo real (WebP, AVIF) provee un benchmark intensivo en SIMD, bandwidth de memoria y cómputo.\n\n```rust\n// Apuntar a Graviton4 específicamente\n// .cargo/config.toml\n[target.aarch64-unknown-linux-gnu]\nrustflags = [\"-C\", \"target-cpu=neoverse-v2\"]\n```\n\n| Operación | x86 (AVX2) | ARM64 (default) | ARM64 (neoverse-v2) | Delta vs x86 |\n| --- | --- | --- | --- | --- |\n| JPEG decode (4K) | 12.3ms | 14.1ms | 11.2ms | -8.9% |\n| WebP encode | 28.7ms | 31.2ms | 24.8ms | -13.6% |\n| AVIF encode | 89.4ms | 97.1ms | 78.3ms | -12.4% |\n| Resize 4K→1080p | 8.4ms | 9.1ms | 7.2ms | -14.3% |\n\nObservar la diferencia entre ARM64 default y ` target-cpu=neoverse-v2 `. Sin el flag de target CPU, ARM64 es en realidad más lento que x86 para workloads SIMD-heavy porque el compilador genera código NEON conservador. Con el flag, usa SVE2 con vectores más amplios y el rendimiento se invierte."},{"heading":"NEON vs SVE2: Diferencias Clave","level":3,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#neon-vs-sve2-diferencias-clave","content":"ARM64 tiene dos sets de instrucciones SIMD:\n\n- **NEON**: Vectores fijos de 128 bits, disponible en todo ARM64. Equivalente a SSE4.2.\n- **SVE2**: Vectores escalables (128-512 bits, Graviton4 usa 128 bits pero con instrucciones adicionales), disponible en ARMv9. Equivalente a AVX2/AVX-512 en capacidad, aunque no en ancho de vector crudo.\n\nSi se está usando intrinsics SIMD directamente en Rust:\n\n```rust\n// NEON (funciona en todos lados)\n#[cfg(target_arch = \"aarch64\")]\nuse std::arch::aarch64::*;\n\nunsafe fn sum_neon(data: &[f32]) -> f32 {\n let mut acc = vdupq_n_f32(0.0);\n for chunk in data.chunks_exact(4) {\n let v = vld1q_f32(chunk.as_ptr());\n acc = vaddq_f32(acc, v);\n }\n vaddvq_f32(acc)\n}\n```\n\nPara la mayoría del código Rust, no se requieren intrinsics. El compilador auto-vectoriza cuando se configura el target CPU correctamente. Sin embargo, al portar código SIMD de x86, es importante considerar que NEON y AVX tienen semánticas diferentes para algunas operaciones (en particular: no existe equivalente directo de ` _mm_shuffle_ps `, se requiere ` vqtbl1q_u8 ` o similar)."},{"heading":"Análisis de Costos: Cuatro Meses en Producción","level":2,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#anlisis-de-costos-cuatro-meses-en-produccin","content":"Después de 4 meses en producción, aquí van los números reales:"},{"heading":"Costos de Cómputo (14 servicios, us-east-1)","level":3,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#costos-de-cmputo-14-servicios-us-east-1","content":"|  | x86 (familia m7i) | ARM64 (familia r8g) | Ahorro |\n| --- | --- | --- | --- |\n| On-Demand mensual | $4,847 | $3,878 | -20.0% |\n| Reserved 1 año mensual | $3,105 | $2,484 | -20.0% |\n| Spot mensual (promedio) | $1,939 | $1,242 | -35.9% |\n\nLos ahorros en Spot son lo destacado. [AWS](<https://aws.com/>) tiene significativamente más capacidad Spot ARM64 (menos gente usándola), así que las tasas de interrupción son más bajas y los precios más estables. En un período de 4 meses, la tasa promedio de interrupción Spot en ARM64 midió 2.1% vs 5.8% en x86."},{"heading":"Ahorro Total en Infraestructura","level":3,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#ahorro-total-en-infraestructura","content":"Combinando la mejora de performance (que permite reducir el tamaño de algunas instancias) con el menor costo por vCPU:\n\n- **Antes (x86, mix On-Demand/Reserved)**: $3,740/mes\n- **Después (ARM64, mix Reserved/Spot)**: $2,180/mes\n- **Ahorro neto**: $1,560/mes (-41.7%)\n\nEse 41.7% no es solo la diferencia de precio ARM64, incluye rightsizing de instancias, ya que los mismos workloads necesitan menos vCPUs en Graviton4."},{"heading":"Checklist de Migración","level":2,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#checklist-de-migracin","content":"Una checklist condensada de migración basada en experiencia de producción:\n\n1. **Imágenes base de containers**: Cambiá a multi-arch o imágenes ` arm64v8/ ` explícitas. ` FROM node:22-slim ` ya pullea la arch correcta automáticamente.\n2. **CI/CD**: Agregar runners ARM64. GitHub Actions ya tiene ` runs-on: ubuntu-24.04-arm `. Si son self-hosted, instancias Graviton3 c7g son excelentes CI runners.\n3. **Auditoría de dependencias nativas**: Ejecutar tu suite de tests en ARM64 primero. Buscá fallbacks WASM, binarios faltantes, requerimientos de compilación.\n4. **Compiler flags**: Configurar target CPU para lenguajes compilados. ` GOARM64=v9.0 ` para Go, ` target-cpu=neoverse-v2 ` para Rust, ` -march=armv9-a ` para C/C++.\n5. **Monitoreo**: Vigilar regresiones de performance en operaciones específicas. Las operaciones crypto comúnmente resultan más lentas inicialmente por falta de configuración del engine OpenSSL para ARM64.\n6. **Rollout gradual**: Utilizar target groups con pese es en tu load balancer. Empezá con 10% de tráfico a ARM64, validá métricas, incrementá."},{"heading":"Limitaciones y Consideraciones de los Benchmarks","level":2,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#limitaciones-y-consideraciones-de-los-benchmarks","content":"Todo post de benchmarks (incluyendo este) es inherentemente engañoso porque mide workloads específicos en configuraciones específicas. Los resultados van a variar. Algunas áreas se excluyen deliberadamente porque serían engañosas:\n\n- **Workloads memory-bound**: El r8g tiene características de RAM diferentes al m7i. La comparación directa es comparar peras con manzanas.\n- **Performance pico single-threaded**: El turbo boost de x86 puede llegar a 3.6GHz+. Graviton4 corre a 3.0GHz fijos. Para ráfagas de single-thread, x86 gana.\n- **Workloads AVX-512**: Si tu código usa AVX-512 (inferencia ML, computación científica), x86 tiene una ventaja genuina. El SVE2 de Graviton4 no es un equivalente directo.\n\nEl enfoque correcto: ejecutar tu workload de producción real en ambas arquitecturas con tráfico real. Los benchmarks sintéticos te dicen dirección, no magnitud."},{"heading":"¿Vale la Pena?","level":2,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#vale-la-pena","content":"La reducción de costos del 41% por sí sola justifica las aproximadamente dos semanas de trabajo de migración. Pero el beneficio menos cuantificable es operacional: los cores de frecuencia fija de Graviton4 entregan performance más predecible, lo que significa que el autoscaling es más responsivo y el capacity planning es más simple.\n\nPara workloads basados en AWS, la evaluación de Graviton4 es cada vez más indispensable. El ecosistema ARM64 está lo suficientemente maduro como para que la migración sea aburrida, y en infraestructura, aburrido es exactamente el objetivo."},{"heading":"Preguntas frecuentes","level":2,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#questions","content":""},{"heading":"¿Graviton4 es siempre más rápido que instancias x86 equivalentes?","level":3,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#question-1","content":"No. Para workloads single-threaded con uso intenso de AVX-512, x86 todavía puede ganar. Graviton4 se destaca en workloads orientados a throughput y multi-threaded, y ofrece mejor performance-por-dólar en casi todos los escenarios testeados."},{"heading":"¿Qué tan difícil es la migración de x86 a ARM64?","level":3,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#question-2","content":"Para lenguajes interpretados (Node.js, Python, Ruby), es mayormente sin dolor, el runtime maneja la diferencia de arquitectura. Para lenguajes compilados, se necesita cross-compilar o compilar en ARM64. Los problemas están en las dependencias nativas y las imágenes base de containers."},{"heading":"¿Qué pasa con las imágenes Docker para ARM64?","level":3,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#question-3","content":"La mayoría de las imágenes Docker oficiales ya proveen manifiestos multi-arch. Para imágenes custom, utilizar Docker buildx con --platform linux/arm64. El mayor dolor de cabeza son imágenes de terceros que solo proveen builds amd64."},{"heading":"Tips","level":2,"url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#article-tips","content":"1. **Flag crítico:** Para Go en Graviton4, compilá con ` GOARM64=v9.0 ` para desbloquear instrucciones SVE2, los builds default dejan 15-20% de performance en la mesa para workloads compute-heavy.\n2. **Trampa de Node.js:** El JIT de V8 en ARM64 no auto-vectoriza tan agresivamente como en x86. Para loops numéricos ajustados, considerá ` --experimental-wasm-simd ` o descargá a addons nativos.\n3. **Truco de costos:** Las instancias Spot de Graviton4 tienen 60-70% menos frecuencia de interrupción que las equivalentes x86, AWS tiene exceso de capacidad ARM. Utilizar estrategia de asignación ` capacity-optimized `.\n4. **Imprescindible para testing:** Siempre correr la suite completa de tests en CI ARM64 antes de migrar. Algunos paquetes npm nativos no tienen binarios ARM64 y caen silenciosamente a builds WASM más lentos.\n\n- arm64\n- graviton4\n- aws\n- benchmarks\n- performance\n- migration\n- nodejs\n- go\n- rust\n\n[Volver al Blog](<https://lucioduran.com/es/blog>)"}],"links":[{"text":"English","url":"https://lucioduran.com/blog/arm64-graviton4-server-migration-benchmarks"},{"text":"AWS","url":"https://aws.com/"},{"text":"Volver al Blog","url":"https://lucioduran.com/es/blog"}],"structuredData":[{"@context":"https://schema.org","@graph":[{"@type":"BlogPosting","@id":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks#article","mainEntityOfPage":{"@type":"WebPage","@id":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks"},"headline":"Benchmarks de Migración a Graviton4 ARM64: Datos Reales de Performance en Producción","description":"Benchmarks de producción de migrar workloads a instancias AWS Graviton4 ARM64. Diferencias de rendimiento en Node.js, Go y Rust, trampas de NEON SIMD, compiler flags que importan, y el ahorro real después de 4 meses.","datePublished":"2025-11-22","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://statics.lucioduran.com/images/lucioduran-social-card.jpg","url":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks","inLanguage":"es","isPartOf":{"@id":"https://lucioduran.com/blog#blog"},"articleSection":"Infrastructure","keywords":"arm64, graviton4, aws, benchmarks, performance, migration, nodejs, go, rust","wordCount":1622,"timeRequired":"PT10M","workTranslation":[{"@type":"BlogPosting","inLanguage":"en","url":"https://lucioduran.com/blog/arm64-graviton4-server-migration-benchmarks"}]},{"@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":"Benchmarks de Migración a Graviton4 ARM64: Datos Reales de Performance en Producción","item":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks"}]},{"@type":"FAQPage","mainEntity":[{"@type":"Question","name":"¿Graviton4 es siempre más rápido que instancias x86 equivalentes?","acceptedAnswer":{"@type":"Answer","text":"No. Para workloads single-threaded con uso intenso de AVX-512, x86 todavía puede ganar. Graviton4 se destaca en workloads orientados a throughput y multi-threaded, y ofrece mejor performance-por-dólar en casi todos los escenarios testeados."}},{"@type":"Question","name":"¿Qué tan difícil es la migración de x86 a ARM64?","acceptedAnswer":{"@type":"Answer","text":"Para lenguajes interpretados (Node.js, Python, Ruby), es mayormente sin dolor, el runtime maneja la diferencia de arquitectura. Para lenguajes compilados, se necesita cross-compilar o compilar en ARM64. Los problemas están en las dependencias nativas y las imágenes base de containers."}},{"@type":"Question","name":"¿Qué pasa con las imágenes Docker para ARM64?","acceptedAnswer":{"@type":"Answer","text":"La mayoría de las imágenes Docker oficiales ya proveen manifiestos multi-arch. Para imágenes custom, utilizar Docker buildx con --platform linux/arm64. El mayor dolor de cabeza son imágenes de terceros que solo proveen builds amd64."}}]}]}],"contentHash":"69ea197b20e9735e25dfa7fb25f70758f27184990bfaaa33acabac717cd0b266","provenance":{"method":"Deterministic serialization of the server-rendered main element","source":"https://lucioduran.com/es/blog/arm64-graviton4-server-migration-benchmarks","hashAlgorithm":"sha256","hashScope":"JSON document fields before contentHash and provenance","sourceHTMLBytes":79844,"contentBytes":14322}}
