¿Qué es .NET Native AOT? Diferencias con JIT y trimming

· Actualizado el: · · C#, .NET, Native AOT, Publicación, Diseño

En Cómo llamar a una DLL de C# Native AOT desde C/C++ ya escribimos sobre cómo llamar a C# desde C/C++ usando Native AOT. Pero, en realidad, habría sido más amable colocar antes qué es Native AOT en primer lugar. El orden ha quedado un poco invertido.

Cuando se habla de Native AOT, lo primero que suele pasar es que los términos se mezclan con facilidad.

  • ¿Se trata de eliminar el JIT?
  • ¿En qué se diferencia de self-contained o single-file?
  • ¿Es de la misma familia que ReadyToRun?
  • ¿Qué está pasando cuando aparecen muchas advertencias de trimming?
  • ¿Se puede usar con el mismo nivel de confianza en WPF, WinForms o ASP.NET Core?

Cuando todo esto se mezcla, Native AOT puede parecer «una magia que simplemente lo hace todo más rápido», o, al contrario, «algo temible lleno de restricciones». Las dos visiones son un poco simplistas.

En este artículo, partiendo sobre todo de la sensación práctica actual desde .NET 8 en adelante, aclaramos primero estos cuatro puntos:

  • La verdadera naturaleza de Native AOT
  • Qué ventajas ofrece y dónde se vuelve complicado
  • En qué se diferencia de ReadyToRun y de trimming
  • Con qué tipo de aplicación conviene empezar a probarlo con calma

Índice

  1. Conclusión rápida (en una frase)
  2. Tabla comparativa para empezar
    • 2.1. Términos relacionados con Native AOT
    • 2.2. Diferencias entre JIT, ReadyToRun y Native AOT
  3. Panorama general de Native AOT (diagrama)
  4. Qué ventajas ofrece Native AOT
    • 4.1. El inicio tiende a ser más ligero
    • 4.2. No es necesario instalar el runtime de antemano
    • 4.3. Es adecuado para entornos de ejecución restringidos
  5. Qué se vuelve complicado con Native AOT
    • 5.1. Reflection y generación dinámica de código
    • 5.2. Hay que pensar partiendo de trimming
    • 5.3. Se publica por plataforma
    • 5.4. El contexto de escritorio Windows / COM requiere mucha cautela
  6. Pasos mínimos
    • 6.1. csproj
    • 6.2. publish
    • 6.3. Cómo escribir JSON
  7. Casos en los que resulta adecuado
  8. Casos en los que no resulta adecuado
  9. Puntos donde uno suele atascarse
  10. Resumen
  11. Referencias

1. Conclusión rápida (en una frase)

  • Native AOT es un método que precompila una aplicación .NET a código nativo en el momento del publish, para distribuirla así.
  • Como no utiliza el JIT en tiempo de ejecución, el tiempo de inicio y el consumo de memoria suelen mejorar, y resulta más fácil distribuirla en entornos sin el runtime de .NET instalado.
  • Sin embargo, se lleva mal con la reflection sin restricciones, la generación dinámica de código, el COM integrado (built-in COM) y las bibliotecas que no admiten trimming.
  • Es decir, más que una magia que hace todo más rápido, es un modelo de publicación que, por conveniencia del arranque, la distribución y el entorno de ejecución, renuncia un poco al mundo dinámico para acercarse a un mundo estático.

Native AOT es «un mecanismo para distribuir .NET de forma parecida a una aplicación nativa», no una simple casilla que marcar para acelerar la compilación.

2. Tabla comparativa para empezar

2.1. Términos relacionados con Native AOT

Antes que nada, conviene separar estos conceptos; así el resto resulta más sencillo.

Término Qué hace Relación con Native AOT
JIT Genera código nativo a partir del IL en tiempo de ejecución Native AOT adelanta este paso antes de la ejecución
self-contained Distribuye también todo el conjunto de .NET necesario para ejecutarse Native AOT se plantea dentro de esta misma familia
single-file Empaqueta el resultado en un único archivo No es la esencia de Native AOT, pero el resultado final suele parecerse
trimming Elimina el código que no se utiliza En Native AOT es prácticamente un requisito
ReadyToRun Conserva el IL y adelanta un poco el trabajo del JIT Se parece a Native AOT, pero es un mecanismo distinto
source generator Traslada el procesamiento dinámico en tiempo de ejecución a generación de código en tiempo de compilación Se lleva bien con Native AOT

Lo que se presta a confusión es que Native AOT no es tanto «una única función» como un modelo de publicación que funciona bien junto con self-contained, trimming, source generation, la publicación fijada a un RID, etc.

2.2. Diferencias entre JIT, ReadyToRun y Native AOT

Aquí también conviene verlo de un vistazo.

Aspecto Ejecución JIT normal ReadyToRun Native AOT
JIT en tiempo de ejecución Se usa Todavía se usa en algunos casos No se usa
Contenido de lo distribuido Centrado en IL IL + código pregenerado Centrado en un ejecutable nativo
Inicio Referencia Suele mejorar Suele mejorar bastante
Compatibilidad La más amplia Amplia Restricciones fuertes
Funciones dinámicas Fáciles de usar En general, fáciles de usar Muy limitadas
Objetivo adecuado Desarrollo .NET habitual en general Mejorar el inicio como primer paso Apostar fuerte por el inicio, la distribución y entornos restringidos

Si ReadyToRun apunta a «aliviar un poco el JIT», Native AOT apunta a «no dar por sentado el JIT en tiempo de ejecución». Aunque ambos llevan la palabra AOT, el matiz es bastante distinto.

Dicho esto, si resumimos en una sola imagen «entonces, ¿qué forma de distribución elegir?», queda así:

Árbol de decisión para elegir la forma de distribuciónDiagrama de flujo que, partiendo de si se puede instalar el runtime de .NET en el destino y de si se depende de reflection, generación dinámica de código o COM integrado, lleva a elegir entre publicación framework-dependent, ReadyToRun, self-contained o Native AOTSe puedeNo tantoSí, quiero reducirloNo quiero / no puedo instalarloDependeNo dependeNoQuiero decidir la forma de distribución¿Se puede instalar el runtime de .NET en el destino?¿Quiero reducir el tiempo de inicio?framework-dependentpublicación normalReadyToRunmejora el inicio manteniendo la compatibilidad¿Depende de reflection, generación dinámica de código o COM integrado?self-containedsi hace falta, se empaqueta como single-file¿El objetivo es una consola, un worker o una API pequeña?Native AOTPrimero self-containedreconsiderarlo tras reducir las dependencias dinámicas

La primera bifurcación es si se puede colocar el runtime en el destino de distribución, y la segunda es hasta qué punto se pueden reducir los mecanismos dinámicos. self-contained y single-file son cuestión de «cómo se distribuye», mientras que ReadyToRun y Native AOT son cuestión de «cuándo se genera el código nativo», así que en la práctica hay que combinarlos.

3. Panorama general de Native AOT (diagrama)

Si dibujamos Native AOT a grandes rasgos, queda así:

Panorama general del flujo de Native AOTDiagrama que compara la ejecución JIT normal, que compila desde IL en tiempo de ejecución, con el flujo de Native AOT, que analiza, recorta y compila a código nativo en el momento del publishEjecución normaldotnet publish + PublishAotCódigo fuente C# / .NETEnsamblado ILJIT en tiempo de ejecuciónEjecución de la aplicaciónAnálisis de AOT / trimEliminación de código innecesarioGeneración de código nativoEjecutable específico del RID

En el .NET habitual, primero se genera el IL y luego se hace JIT solo de lo necesario en tiempo de ejecución. Native AOT adelanta al momento del publish una gran parte de esa etapa posterior.

Lo importante aquí es que, en el momento del publish, hay que conocer casi todo el código que se va a necesitar en tiempo de ejecución. Esto cambia las premisas sobre qué formas de escribir código están permitidas.

  • Buscar un tipo en tiempo de ejecución
  • Generar código en tiempo de ejecución
  • Cargar un Assembly en tiempo de ejecución
  • Resolver algo de forma diferida en tiempo de ejecución confiando en que «ya se resolverá de alguna manera»

Este tipo de estilos de código pasan a llevarse mal con Native AOT de golpe.

4. Qué ventajas ofrece Native AOT

4.1. El inicio tiende a ser más ligero

El efecto más evidente de Native AOT sigue siendo el inicio.

  • Herramientas de línea de comandos
  • Procesos de vida corta
  • Arranques de tipo serverless
  • Inicio e intercambio de contenedores
  • Herramientas de monitorización o pequeños procesos residentes

En estos casos el coste del JIT se nota con facilidad, y como Native AOT lo adelanta, el arranque resulta más ligero.

El consumo de memoria también suele mejorar, lo cual es una ventaja cuando se quiere apretar la misma cantidad de instancias en el mismo espacio. En particular, en el lado de la nube, donde se levantan muchas instancias del mismo proceso, esta diferencia se nota poco a poco.

4.2. No es necesario instalar el runtime de antemano

Una aplicación publicada con Native AOT es más fácil de ejecutar incluso en entornos sin el runtime de .NET instalado.

Esto, aunque discreto, es bastante importante.

  • No se quiere decir al destino «instale primero el runtime de .NET 9»
  • Se quiere adelgazar la imagen del contenedor
  • Se quiere colocar y ejecutar una sola herramienta pequeña, sin más
  • No se quiere permitir JIT en el entorno de ejecución, o no está permitido

En estos casos, el simple hecho de no tener la premisa de «hay que preparar el runtime por separado» ya calma bastante las cosas.

Aquí, «no requiere runtime» significa que no hace falta instalar .NET aparte en el destino de distribución. No significa que la parte equivalente al runtime desaparezca por completo de dentro de la aplicación.

4.3. Es adecuado para entornos de ejecución restringidos

Como Native AOT no usa el JIT en tiempo de ejecución, es más fácil de ejecutar incluso en entornos donde el JIT no está permitido.

Esto resulta más útil en el terreno de la nube, los contenedores y lo relacionado con móviles que en el escritorio. Aun así, incluso en el contexto del desarrollo para Windows, tiene su gracia en el sentido de que «se quieren reducir premisas innecesarias en el destino de distribución».

5. Qué se vuelve complicado con Native AOT

5.1. Reflection y generación dinámica de código

La restricción principal de Native AOT está aquí.

  • Cargas dinámicas como Assembly.LoadFile
  • Generación de código en tiempo de ejecución como System.Reflection.Emit
  • Reflection que recorre tipos sin límite en tiempo de ejecución
  • Formas de escribir código que ensamblan generics libremente en tiempo de ejecución

En estos casos resulta difícil fijar en el momento del publish qué código será necesario, y eso se convierte en el caldo de cultivo de las advertencias de AOT.

Por supuesto, no es tan simple como que basta con una sola línea de reflection para que todo falle. Pero la tendencia de que cuanto más se orienta el diseño a «verlo y decidirlo en tiempo de ejecución», más complicado se vuelve no falla.

Lo que suele aparecer en el nombre de las advertencias es la familia RequiresDynamicCode. Esto significa que «esa llamada podría romperse con AOT», así que no conviene suprimirlo a la ligera.

Al trabajar con Native AOT, ayuda pensarlo como: reducir la «inteligencia en tiempo de ejecución» y aumentar la «explicitud en tiempo de compilación».

5.2. Hay que pensar partiendo de trimming

Native AOT está profundamente ligado a trimming. Aquí es fácil pasar por alto que no solo influye el propio código, sino también cómo está escrito el de las bibliotecas de las que se depende.

Conviene prestar atención a lo siguiente:

  • Serializadores basados en reflection
  • Configuraciones de DI o de plugins que recopilan tipos mediante escaneo en tiempo de ejecución
  • Mecanismos que buscan un tipo a partir de un nombre en cadena de texto y lo instancian
  • Bibliotecas que dependen de proxies dinámicos o de generación de IL

Si aparece una advertencia y se da por bueno con un «total, el publish ha pasado», más adelante suele salir bastante caro. En Native AOT, en general conviene leerse las advertencias en serio.

Aun así, con un simple «léelas en serio» no se puede avanzar, así que dejamos por escrito un orden de prioridad para abordarlas. La documentación oficial también recomienda probarlas en este orden.

Orden Solución Cuándo usarla
1 Dejar de usar reflection Se puede sustituir Activator.CreateInstance(Type) por un argumento genérico, o trasladarlo a un source generator
2 Añadir DynamicallyAccessedMembers Se necesita reflection, pero el tipo objetivo se conoce en tiempo de compilación
3 Añadir RequiresUnreferencedCode El nombre del tipo se decide mediante una cadena en tiempo de ejecución, entre otros casos donde el análisis estático es, por naturaleza, imposible
4 Suprimirlo con UnconditionalSuppressMessage Último recurso, cuando nada de lo anterior es viable y se ha confirmado que es seguro

El primer paso más habitual es el patrón 2, «el tipo se conoce, pero solo aparece la advertencia». Por ejemplo, el siguiente código genera IL2070.

// IL2070: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods'
void PrintMethodNames(Type type)
{
    foreach (var method in type.GetMethods())
    {
        Console.WriteLine(method.Name);
    }
}

Si se declara mediante un atributo que, al llamar a GetMethods(), se quieren conservar los métodos públicos, la advertencia desaparece.

using System.Diagnostics.CodeAnalysis;

void PrintMethodNames(
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{
    foreach (var method in type.GetMethods())
    {
        Console.WriteLine(method.Name);
    }
}

// Si quien llama pasa el valor con typeof, el requisito se cumple automáticamente
PrintMethodNames(typeof(DateTime));

La API que se llama y la especificación necesaria suelen corresponderse: GetMethod / GetMethods con PublicMethods; GetProperty / GetProperties con PublicProperties; Activator.CreateInstance con PublicParameterlessConstructor o PublicConstructors. DynamicallyAccessedMemberTypes.All es cómodo, pero conserva todos los miembros del tipo objetivo, lo que infla el tamaño, y los miembros conservados arrastran otras advertencias, así que el principio es especificar lo mínimo necesario.

Además, cuando añadir el atributo no hace desaparecer la advertencia, hay que remontar desde el punto donde se usa reflection hasta quien lo llama, y comprobar que está presente en todo el recorrido. Si falta en un solo punto intermedio, el requisito se corta ahí.

5.3. Se publica por plataforma

Native AOT se publica con el RID (Runtime Identifier) fijado. Es decir, no es un mundo en el que algo construido para win-x64 funcione tal cual en linux-x64.

  • Windows x64
  • Windows Arm64
  • Linux x64
  • Linux Arm64
  • macOS Arm64

La premisa es generar un resultado de publicación para cada destino, como en la lista anterior.

Aquí la sensación se acerca mucho más a la de «una aplicación nativa» que en un .NET framework-dependent habitual.

5.4. El contexto de escritorio Windows / COM requiere mucha cautela

En el contexto de KomuraSoft, esto es especialmente importante.

En Windows, Native AOT no tiene COM integrado (built-in COM). Además, WPF se lleva mal con trimming y WinForms depende mucho del marshalling de COM integrado, así que, al menos por ahora, conviene ver ambas con bastante cautela como «primera candidata a Native AOT».

Dejamos por escrito el criterio de «por ahora» al que nos referimos aquí. Lo que se dice en este artículo se basa en la documentación oficial de las generaciones .NET 8 / 9 / 10 (a fecha de julio de 2026). Sobre WPF y WinForms, en el apartado «Known trimming incompatibilities» de Microsoft se indica expresamente que WPF depende mucho de reflection y de la inspección de código en tiempo de ejecución, por lo que prácticamente deja de funcionar tras el trimming, y que WinForms depende mucho del marshalling de COM integrado; por eso, en ambos casos el propio SDK de .NET tiene deshabilitado el soporte de trimming. Es decir, no se trata de «conviene verlo con cautela», sino de que, en el estado actual, es el propio SDK el que lo bloquea. Esta descripción podría cambiar en el futuro, así que conviene revisar la misma página en el momento de la lectura.

En resumen,

  • Convertir de golpe el propio cuerpo de WPF / WinForms a Native AOT
  • Traer la interoperabilidad COM tal cual, con la misma sensación de siempre

En estos casos las advertencias del publish y las restricciones en tiempo de ejecución aumentan de golpe, y el coste de adaptación tiende a dispararse.

En cambio,

  • Consola
  • Worker
  • API web pequeña
  • Componentes que, dentro de la integración nativa, se pueden acercar a la frontera de funciones en C

resultan una entrada más natural.

Si se necesita COM, en algunos casos resulta más razonable seguir con JIT o rediseñar la aplicación partiendo de ComWrappers o de COM generado por source generator.

6. Pasos mínimos

Antes de nada, el publish de Native AOT requiere una cadena de herramientas nativa. Si solo se añade PublishAot y se ejecuta dotnet publish, el fallo no ocurre en la compilación, sino en el enlazado nativo final. Como este es el primer obstáculo, conviene instalarla de antemano.

Entorno Qué se necesita
Windows Visual Studio 2022 o posterior. Instalar la carga de trabajo «Desarrollo de escritorio con C++» con todos sus componentes predeterminados
Ubuntu 18.04 o posterior sudo apt-get install clang zlib1g-dev
Alpine 3.15 o posterior sudo apk add clang build-base zlib-dev
Fedora 39 o posterior / RHEL 8 o posterior sudo dnf install clang zlib-ng-devel zlib-ng-compat-devel zlib-devel
macOS Command Line Tools de Xcode (compatible desde .NET 8)

En resumen, se trata de la cadena de herramientas del compilador y de los paquetes de desarrollo de las bibliotecas de las que depende el runtime de .NET. Si se va a ejecutar en CI, en las muestras de Native AOT de dotnet/samples hay Dockerfiles tanto para Linux como para Windows, así que lo más rápido es tomar de ahí los pasos de instalación de los requisitos.

Además, un binario compilado en Linux solo funciona en la misma versión de Linux o en una más nueva. Lo compilado en Ubuntu 20.04 funciona en 20.04 en adelante, pero no en 18.04. La elección del entorno de compilación determina directamente el alcance de la distribución posible.

6.1. csproj

Primero se añade PublishAot al archivo de proyecto.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <PublishAot>true</PublishAot>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
</Project>

Con net8.0 basta para el ejemplo. La idea en sí es prácticamente la misma en .NET 9 / 10.

Lo importante es dejarlo habitualmente en el proyecto y observar a diario el análisis en build / publish, en lugar de añadirlo solo temporalmente en la línea de comandos de dotnet publish.

Además, aunque se añada <PublishAot>true</PublishAot>, la ejecución local habitual no pasa a ser Native AOT de repente. El dotnet run del día a día y la ejecución normal siguen usando JIT; el verdadero momento de la compilación Native AOT es el publish.

6.2. publish

Por ejemplo, para Windows x64 sería así.

dotnet publish -c Release -r win-x64

Para Linux x64, así.

dotnet publish -c Release -r linux-x64

La salida queda fijada al RID. La perspectiva cambia de «una sola DLL de .NET que funciona en cualquier sitio» a «un ejecutable construido para ese sistema operativo y esa arquitectura».

Si se aborda desde el lado de una API web, resulta más cómodo partir de la plantilla pensada para Native AOT.

dotnet new webapiaot -o MyFirstAotWebApi

Para un worker, así.

dotnet new worker -o WorkerWithAot --aot

6.3. Cómo escribir JSON

Algo con lo que se tropieza a menudo, sin hacer mucho ruido, en Native AOT es el JSON. System.Text.Json, si se usa con la sensación habitual, tiende a apoyarse en reflection, así que resulta más tranquilo orientarlo hacia source generation.

using System.Text.Json;
using System.Text.Json.Serialization;

[JsonSerializable(typeof(AppConfig))]
internal partial class AppJsonContext : JsonSerializerContext
{
}

public sealed class AppConfig
{
    public string? Name { get; init; }
    public int RetryCount { get; init; }
}

var config = new AppConfig
{
    Name = "sample",
    RetryCount = 3
};

string json = JsonSerializer.Serialize(config, AppJsonContext.Default.AppConfig);

En la práctica, más que pensarlo como «compatibilidad con Native AOT», conviene recordarlo como orientarse hacia «no dejar que se busquen tipos en tiempo de ejecución»; así es más difícil equivocarse.

7. Casos en los que resulta adecuado

Native AOT tiende a encajar bien en casos como estos.

  • CLI o herramientas donde el inicio es el protagonista
  • APIs pequeñas desplegadas en gran cantidad en contenedores
  • Workers o servicios en segundo plano
  • Procesos serverless o de vida corta
  • Pequeños componentes .NET que se insertan en aplicaciones nativas
  • Situaciones en las que no se quiere exigir la instalación previa del runtime de .NET en el entorno de ejecución

Lo que tienen en común es que los límites están relativamente bien definidos y resulta fácil reducir los mecanismos dinámicos.

8. Casos en los que no resulta adecuado

Al contrario, también hay casos claros en los que, desde el principio, más vale no convertir a Native AOT en el campo de batalla principal.

  • El cuerpo de grandes aplicaciones existentes en WPF / WinForms
  • Configuraciones que dan por hecha la interoperabilidad COM integrada (built-in COM interop)
  • Aplicaciones cuyo eje central es la carga de plugins en tiempo de ejecución
  • Configuraciones muy dependientes de frameworks que buscan tipos mediante reflection
  • Bibliotecas que usan System.Reflection.Emit o proxies dinámicos como algo normal
  • Diseños que pasan por C++/CLI

En estos casos resulta más razonable el .NET habitual basado en JIT, o ReadyToRun, o revisar cómo se separa el diseño.

9. Puntos donde uno suele atascarse

Por último, resumimos los puntos en los que es fácil tropezar al empezar con Native AOT.

  • Tomarse a la ligera las advertencias del publish
    • Como se ha dicho antes, las advertencias de Native AOT son algo que hay que leer en serio.
  • El build pasa, pero falla en el publish
    • En el publish se ejecuta de verdad un análisis que incluye también las bibliotecas de las que se depende, así que ahí aparecen cosas que antes no se veían.
  • Tratar ReadyToRun y Native AOT con la misma sensación
    • Los términos se parecen, pero la fuerza de las restricciones es bastante distinta.
  • Empezar de golpe por el cuerpo de una aplicación de escritorio
    • Es más tranquilo empezar primero por una consola, un worker o una API pequeña.
  • Escribir el JSON o el enlace de configuración con la costumbre habitual
    • Las formas de escribir código que dan por hecha la reflection acaban pasando factura más adelante.
  • Distribuir pensando que es independiente de la plataforma
    • El resultado de la publicación de Native AOT queda fijado al RID.
  • Pensar que «Native AOT = todo se vuelve más rápido»
    • Los protagonistas son el inicio, la distribución y el entorno de ejecución. Si se pierde esto de vista, las expectativas se desajustan.

En Native AOT, lo que decide en última instancia si algo es viable no es dotnet build, sino dotnet publish. Empezar a ejecutarlo pronto y con frecuencia hace que sea más difícil tener problemas en la recta final.

10. Resumen

Si hay que resumir Native AOT en una frase, es un mecanismo que traslada una aplicación .NET de un modelo de ejecución dinámico a un modelo de distribución que se puede fijar de forma estática.

Los puntos que conviene tener presentes se reducen a estos cinco.

  1. Native AOT precompila a código nativo en el momento del publish
  2. Funciona muy bien para el inicio, la memoria y la conveniencia de la distribución
  3. A cambio, es exigente con reflection, la generación dinámica de código, el COM integrado y el código que no admite trimming
  4. Como primer objetivo, es más tranquilo empezar por una consola, un worker o una API pequeña que por el cuerpo de una aplicación de escritorio
  5. Es importante ir eliminando advertencias mientras se verifica pronto todo, basándose en el publish

Native AOT no es un interruptor estándar que se añada a todas las aplicaciones .NET. Pero en escenarios donde el inicio importa, se quiere aligerar la distribución y se quieren reducir las premisas del entorno de ejecución, es un arma bastante potente.

Al contrario, en el mundo denso de WPF / WinForms / COM, todavía hay muchos casos en los que el .NET habitual resulta más razonable. Si se sabe distinguir esto, Native AOT deja de ser una «función nueva y difícil» para convertirse en una opción con un uso claramente definido.

11. Referencias

Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.

Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.

El artículo está directamente relacionado con los siguientes servicios.

Preguntas frecuentes

Preguntas habituales en las consultas sobre el tema del artículo.

¿Qué es Native AOT?
Es un modo de publicación que precompila una aplicación .NET a código nativo en el momento del publish, en lugar de distribuir IL. Como no utiliza el JIT en tiempo de ejecución, el tiempo de inicio y el consumo de memoria suelen mejorar, y resulta más fácil distribuir la aplicación en entornos sin el runtime de .NET instalado. A cambio, se lleva mal con la reflection sin restricciones, la generación dinámica de código, el COM integrado (built-in COM) y las bibliotecas que no admiten trimming. Más que una magia que hace todo más rápido, es un modelo de publicación que, por conveniencia de arranque, distribución y entorno de ejecución, renuncia un poco al mundo dinámico para acercarse a un mundo estático.
¿En qué se diferencian Native AOT y ReadyToRun?
ReadyToRun es un método que conserva el IL y adelanta parte del trabajo del JIT, de modo que todavía hay situaciones en las que se usa el JIT en tiempo de ejecución. La compatibilidad es amplia y las funciones dinámicas siguen siendo, en general, fáciles de usar. Native AOT, en cambio, no da por sentado el JIT en tiempo de ejecución: el resultado de la publicación se centra en un ejecutable nativo, el inicio suele mejorar mucho más, pero a cambio las restricciones son más fuertes. Aunque ambos comparten la palabra AOT, ReadyToRun apunta a 'aliviar un poco el JIT' mientras que Native AOT apunta a 'no dar por sentado el JIT', y el matiz entre ambos es bastante distinto.
¿Se puede usar Native AOT en aplicaciones WPF o WinForms?
Por ahora conviene tratarlo con bastante cautela. Native AOT en Windows no tiene COM integrado (built-in COM); WPF se lleva mal con trimming y WinForms depende mucho del marshalling de COM integrado, así que ninguna de las dos es una buena primera candidata para Native AOT. Si se necesita COM, en algunos casos resulta más razonable seguir con JIT o rediseñar la aplicación partiendo de ComWrappers o de COM generado por source generator. Como punto de entrada, resulta más natural empezar por una consola, un worker o una API web pequeña.
¿Para qué tipo de aplicaciones es adecuado Native AOT?
Es adecuado para herramientas de línea de comandos donde el inicio es lo importante, APIs pequeñas desplegadas en gran cantidad en contenedores, workers o servicios en segundo plano, procesos serverless o de vida corta, pequeños componentes .NET que se insertan en aplicaciones nativas, y situaciones en las que no se quiere exigir la instalación previa del runtime de .NET en el entorno de ejecución. Lo que tienen en común es que los límites están relativamente bien definidos y resulta fácil reducir los mecanismos dinámicos. Por el contrario, no es adecuado para aplicaciones cuyo eje central es la carga de plugins en tiempo de ejecución, ni para configuraciones muy dependientes de frameworks que buscan tipos mediante reflection.

Perfil del autor

Página de presentación del autor del artículo.

Go Komura

Representante de KomuraSoft LLC

Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.

Volver al blog