Nota
O acceso a esta páxina require autorización. Pode tentar iniciar sesión ou modificar os directorios.
O acceso a esta páxina require autorización. Pode tentar modificar os directorios.
En este artículo se describen las nuevas características del entorno de ejecución de .NET para .NET 11. Se actualizó por última vez para la versión preliminar 4.
Requisitos mínimos de hardware actualizados
Los requisitos mínimos de hardware para .NET 11 se han actualizado para requerir conjuntos de instrucciones más modernos en arquitecturas x86/x64 y Arm64. Además, los destinos de compilación ReadyToRun (R2R) se han actualizado para aprovechar las ventajas de las funcionalidades de hardware más recientes.
Requisitos de Arm64
Para Apple, no hay ningún cambio en el hardware mínimo ni en el objetivo ReadyToRun. Los chips M1 de Apple son aproximadamente equivalentes a armv8.5-a y proporcionan soporte para al menos los AdvSimd conjuntos de instrucciones (NEON), CRC, DOTPROD, LSE, RCPC, RCPC2y RDMA .
Para Linux, no hay ningún cambio en el hardware mínimo. .NET sigue admitiendo dispositivos como Raspberry Pi que solo pueden proporcionar compatibilidad con el conjunto de instrucciones AdvSimd. El objetivo ReadyToRun se ha actualizado para incluir el conjunto de LSE instrucciones, lo que podría resultar en una sobrecarga adicional de jitting si lanzas una aplicación.
Para Windows, la línea base se actualiza para requerir el conjunto de instrucciones LSE. Esto es requerido por Windows 11 y por todas las CPU Arm64 compatibles oficialmente con Windows 10. Además, está alineado con los requisitos de Arm SBSA (Server Base System Architecture). El destino ReadyToRun se ha actualizado para que sea armv8.2-a + RCPC, que proporciona compatibilidad con al menos AdvSimd, , CRCLSE, RCPC, y RDMA, y cubre la mayoría del hardware admitido oficialmente.
| Sistema operativo | JIT/AOT mínimo anterior | Nuevo JIT/AOT mínimo | Destino de R2R anterior | Nuevo destino de R2R |
|---|---|---|---|---|
| Manzana | Apple M1 | (Sin cambios) | Apple M1 | (Sin cambios) |
| Linux | armv8.0-a | (Sin cambios) | armv8.0-a | armv8.0-a + LSE |
| Windows | armv8.0-a | armv8.0-a + LSE | armv8.0-a | armv8.2-a + RCPC |
Requisitos de x86/x64
Para los tres sistemas operativos (Apple, Linux y Windows), la línea base se actualiza de x86-64-v1 a x86-64-v2. Esto cambia el hardware de solo garantizar CMOV, CX8, SSE y SSE2 a también garantizar CX16, POPCNT, SSE3, SSSE3, SSE4.1 y SSE4.2. Esta garantía es requerida por Windows 11 y por todas las CPU x86/x64 compatibles oficialmente con Windows 10. Incluye todos los chips aún admitidos oficialmente por Intel y AMD, con los últimos chips más antiguos que han salido de soporte alrededor de 2013.
El destino ReadyToRun se ha actualizado a x86-64-v3 para Windows y Linux, que además incluye los AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT y MOVBE conjuntos de instrucciones. El destino ReadyToRun para Apple permanece sin cambios.
| Sistema operativo | JIT/AOT mínimo anterior | Nuevo JIT/AOT mínimo | Destino de R2R anterior | Nuevo destino de R2R |
|---|---|---|---|---|
| Manzana | x86-64-v1 | x86-64-v2 | x86-64-v2 | (Sin cambios) |
| Linux | x86-64-v1 | x86-64-v2 | x86-64-v2 | x86-64-v3 |
| Windows | x86-64-v1 | x86-64-v2 | x86-64-v2 | x86-64-v3 |
Impacto
A partir de .NET 11, .NET no se puede ejecutar en hardware anterior y podría imprimir un mensaje similar al siguiente:
La CPU actual carece de uno o más de los conjuntos de instrucciones de línea base.
En el caso de los ensamblados compatibles con ReadyToRun, puede haber una sobrecarga de inicio adicional en algún hardware compatible que no cumpla la compatibilidad esperada con un dispositivo típico.
Motivo del cambio
.NET admite una amplia gama de hardware, a menudo por encima y más allá de los requisitos mínimos de hardware establecidos por el sistema operativo subyacente. Esta compatibilidad agrega una complejidad significativa al código base, especialmente para hardware mucho más antiguo que es poco probable que todavía esté en uso. Además, define un "mínimo común denominador" al que los objetivos de AOT deben ajustarse por defecto, lo que, en algunos escenarios, puede dar lugar a una reducción del rendimiento.
La actualización a la línea base mínima se realizó para reducir la complejidad de mantenimiento del código base y para alinearse mejor con los requisitos de hardware documentados (y a menudo aplicados) del sistema operativo subyacente.
Para obtener más información, consulte Requisitos mínimos de hardware actualizados.
Sincronización en tiempo de ejecución
.NET 11 presenta la asincronía nativa en tiempo de ejecución (Runtime Async V2), un paso importante para reemplazar las máquinas de estado asincrónicas generadas por el compilador con suspensión administrada en tiempo de ejecución y reanudación. En lugar de que el compilador genere clases de máquina de estados, el entorno de ejecución realiza un seguimiento de la ejecución asincrónica, produciendo seguimientos de pila más limpios, mejor capacidad de depuración y menor sobrecarga.
Runtime Async es una característica en versión preliminar. Para participar, agregue la siguiente propiedad al archivo del proyecto:
<PropertyGroup>
<Features>runtime-async=on</Features>
</PropertyGroup>
Un net11.0 proyecto ya no requiere <EnablePreviewFeatures>true</EnablePreviewFeatures> usar Async en tiempo de ejecución.
Las bibliotecas en tiempo de ejecución de .NET se compilan con runtime-async=on. Las bibliotecas en tiempo de ejecución ya no contienen máquinas de estado generadas por el compilador y dependen completamente de la asincrónica proporcionada por el entorno de ejecución. Esto permite migrar una aplicación completa (solo con dependencias de biblioteca) al nuevo modelo y proporciona una amplia validación funcional y de rendimiento de la característica. El equipo del producto da la bienvenida a cualquier informe (positivo o negativo) sobre los cambios de rendimiento y tamaño de biblioteca que observe.
.NET 11 también incluye dos mejoras adicionales:
-
Redefiniciones covariantes
Task→Task<T>: Cuando una clase derivada devuelveTask<T>para un método base que devuelveTask, el entorno de ejecución ahora genera un thunk con valor de retorno void que salva la diferencia de convención de llamada, por lo que el despacho virtual funciona para ambas variantes. La misma corrección se aplica a NativeAOT. - Inserción en crossgen2: Se han quitado las restricciones que impedían que los métodos asincrónicos en tiempo de ejecución se insertaran durante la compilación ReadyToRun (R2R). Todas las pruebas asincrónicas se pasan tanto con crossgen2 como con R2R compuesto, e inclusión de llamadas asincrónicas sin espera (la ruta de acceso rápida sincrónica) se confirma de un extremo a otro.
Nota:
Las DOTNET_RuntimeAsync variables de entorno y UNSUPPORTED_RuntimeAsync que controlaban previamente el comportamiento asincrónico en tiempo de ejecución se han quitado. Para deshabilitar runtime-async para un proyecto, establezca <UseRuntimeAsync>false</UseRuntimeAsync> en el archivo del proyecto en lugar de depender de la variable de entorno.
Pistas de pila vivas más limpias
La mejora más visible está en los rastreosde pila en vivo: qué perfiladores, depuradores y new StackTrace() ven durante la ejecución. Con asíncronas generadas por compiladores, cada método asíncrono produce múltiples tramas a partir de la infraestructura de la máquina de estados. Con Runtime Async, los métodos reales aparecen directamente en la pila de llamadas.
// To enable runtime async, add the following to your .csproj:
// <Features>runtime-async=on</Features>
await OuterAsync();
static async Task OuterAsync()
{
await Task.CompletedTask;
await MiddleAsync();
}
static async Task MiddleAsync()
{
await Task.CompletedTask;
await InnerAsync();
}
static async Task InnerAsync()
{
await Task.CompletedTask;
Console.WriteLine(new StackTrace(fNeedFileInfo: true));
}
Sin runtime-async—13 fotogramas, la infraestructura de la máquina de estados es visible:
at Program.<<Main>$>g__InnerAsync|0_2() in Program.cs:line 24
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](...)
at Program.<<Main>$>g__InnerAsync|0_2()
at Program.<<Main>$>g__MiddleAsync|0_1() in Program.cs:line 14
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](...)
at Program.<<Main>$>g__MiddleAsync|0_1()
at Program.<<Main>$>g__OuterAsync|0_0() in Program.cs:line 8
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](...)
at Program.<<Main>$>g__OuterAsync|0_0()
at Program.<Main>$(String[] args) in Program.cs:line 3
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](...)
at Program.<Main>$(String[] args)
at Program.<Main>(String[] args)
Con runtime-async—5 marcos, la cadena de llamadas real:
at Program.<<Main>$>g__InnerAsync|0_2() in Program.cs:line 24
at Program.<<Main>$>g__MiddleAsync|0_1() in Program.cs:line 14
at Program.<<Main>$>g__OuterAsync|0_0() in Program.cs:line 8
at Program.<Main>$(String[] args) in Program.cs:line 3
at Program.<Main>(String[] args)
Nota:
Las trazas de la pila de excepciones (de catch (Exception ex)) ya se ven igual con o sin Async en tiempo de ejecución, porque la limpieza existente ExceptionDispatchInfo en el código generado por compiladores gestiona ese caso. La mejora está en lo que observas durante la ejecución en vivo.
Esta mejora beneficia a cualquier cosa que inspeccione la pila de ejecución en vivo, incluyendo herramientas de perfilado, registros de diagnóstico y la ventana de la pila de llamadas al depurador.
Compatibilidad con NativeAOT y ReadyToRun
Runtime Async admite la compilación NativeAOT y ReadyToRun. Esto extiende la funcionalidad más allá del código compilado JIT a escenarios compilados anticipadamente. El runtime también reutiliza los objetos de continuación de forma más agresiva y evita guardar locales sin cambios, reduciendo la presión de asignación en código asíncrono.
Mejoras en la depuración
Los puntos de interrupción ahora se vinculan correctamente dentro de métodos asíncronos en tiempo de ejecución, y el depurador puede atravesar await los límites sin tener que entrar en una infraestructura generada por el compilador.
Mejoras de JIT
-
Eliminación de la comprobación de límites: El compilador Just-In-Time (JIT) ahora elimina las comprobaciones de límites del patrón común en el que se compara un índice más una constante con una longitud, como
i + cns < len. También elimina comprobaciones de límites más redundantes para el acceso indexado desde el extremo (por ejemplo,values[^1]). Estas mejoras reducen las comprobaciones redundantes en bucles ajustados y mejoran el rendimiento de las operaciones de matriz y segmento. - Eliminación de contextos comprobados redundantes: El JIT ahora puede demostrar y quitar contextos aritméticos comprobados redundantes, por ejemplo, cuando ya se sabe que un valor está en el intervalo. Esta optimización elimina las comprobaciones de desbordamiento innecesarias en el código generado.
-
Plegamiento de expresiones por interruptor: Las expresiones multi-objetivo
switchahora se pliegan en comprobaciones sin ramificación más simples cuando los objetivos son un conjunto pequeño de constantes, por ejemplox is 0 or 1 or 2 or 3 or 4. -
Lanzamiento más rápido de uint-a-float/doble fundición: Casting
uintafloatodoublees más rápido en hardware x86 pre-AVX-512. - Devirtualización en imágenes ReadyToRun: Las imágenes ReadyToRun (R2R) ahora pueden desvirtualizar llamadas a métodos virtuales genéricos no compartidos, lo que mejora el rendimiento del código compilado de antemano para escenarios genéricos.
-
Intrínsecos SVE2: Nuevos intrínsecos de SVE2 (Scalable Vector Extension 2) Arm están disponibles:
ShiftRightLogicalNarrowingSaturate(Even|Odd). Esto amplía el conjunto de operaciones vectorizadas disponibles en el hardware de Arm que admite SVE2.
Para mejorar el rendimiento y la calidad del código, .NET 11 agrega varias optimizaciones JIT más:
Secuencia de plegado constanteEqual
El JIT ahora puede plegar una llamada a string.Equals o ReadOnlySpan<T>.SequenceEqual cuyos operandos sean ambos constantes de tiempo de compilación, sustituyendo la comparación byte a byte por el resultado constante true o false. Esto importa más después de la inserción, cuando un autor de la llamada pasa otro literal a un asistente que se compara con una cadena conocida. Cuando IsAdmin se integra en una función que llama y pasa "Guest", el JIT ve que "Guest" == "Admin" y lo reduce a false:
static bool IsAdmin(string role) => role == "Admin";
La optimización se aplica a literales de cadena, const string campos y literales UTF-8 (por ejemplo, "PNG"u8).
Eliminación de las comprobaciones de límites después de una protección de intervalos vacíos
El JIT ahora recoge la length != 0 aserción de una comprobación de intervalo vacío y la usa para demostrar que la comprobación de límites en el primer elemento se realiza correctamente:
if (!span.IsEmpty && span[0] == value)
{
// The bounds check on span[0] is now eliminated.
}
Eliminación de bifurcaciones y pruebas redundantes
Cuando un predicado externo ya está implícito en una rama interna, el JIT ahora quita la comprobación externa redundante. De forma similar, cuando el código asigna un valor en un condicional y, a continuación, lo prueba inmediatamente, se elimina la segunda prueba:
if (x > 0)
{
if (x > 1) S(); // The outer x > 0 check is folded away.
}
int y = condition ? 1 : 2;
if (y == 1) A(); else B(); // The y == 1 test is eliminated;
// each branch of the ternary goes directly to A() or B().
Estas optimizaciones son más visibles después de la inserción, donde los guardias de diferentes métodos llegan al mismo cuerpo compilado.
Intrínsecos de hardware y generación de código
.NET 11 incluye varias nuevas mejoras intrínsecas de hardware y generación de código:
-
Aceleración F16C para
Half↔floatconversiones en x64: cuando la CPU admite F16C (la mayoría del hardware compatible con AVX2), las conversiones entre Half yfloat/doubleahora usan las instrucciones dedicadasvcvtph2ps/vcvtps2phen lugar de las llamadas auxiliares. - Mejor modelo de costes para SIMD x86/x64: Los costes de ejecución en coma flotante y de tamaño del JIT reflejaban anteriormente los supuestos de la era x87. Los costes actualizados, que reflejan el hardware SSE/AVX moderno, permiten al JIT tomar mejores decisiones sobre la elevación de código y la eliminación de subexpresiones comunes (CSE) en torno al código SIMD.
-
Más rápido
DotProducten AVX: La fase de lowering de las operaciones de tipoVector128.Dotahora genera una secuenciamul + permute + adden lugar devdpps/vdppdcuando AVX está disponible, que es sistemáticamente más rápida. -
Más rápidas
IndexOfAnyAsciiSearcheren Arm64: las versiones de Arm64 deVector*.Count,IndexOfyLastIndexOfya no pasan porExtractMostSignificantBits, lo que supone una mejora del 5 al 50 % en las cargas de trabajo que utilizan estas API en su bucle interno. -
Arm64
ToScalarpara enteros de 64 bits:ToScalarenVector*<long>yVector*<ulong>ahora usafmoven lugar deumov, que es más corto y más rápido en la mayoría de los núcleos. -
Ampliación de la API de SVE
CreateWhile:CreateWhileincorpora variantes con signo,doubleysinglejunto con las ya existentes sin signo, completando el conjunto de generación de predicados para los bucles de SVE. -
Nueva detección de conjuntos de instrucciones SVE2 y Arm64: El tiempo de ejecución ahora notifica
SVE_AES,SVE_SHA3,SVE_SM4,SHA3ySM4como conjuntos de instrucciones independientes, lo que permiteSve2*.IsSupportedconsultas para esas características en hardware compatible. - Corrección de detección de AVX10: La caché de CPUID-bit para AVX10 se sobrescribía mediante una consulta posterior, lo que podría provocar que AVX10 se notificó erróneamente en hardware compatible. Este problema ya está solucionado.
Mejoras de ReadyToRun
Comparer<T>.Default y EqualityComparer<T>.Default ahora están especializados en imágenes ReadyToRun (R2R). Anteriormente, los comparadores predeterminados usaban reflexión que R2R no podía ver con antelación, lo que hacía que las llamadas tuvieran que recurrir al JIT. R2R ahora genera un asistente especializado en la imagen, en línea con el enfoque de NativeAOT, y las pruebas comparativas muestran una mejora de hasta 20× en las operaciones con colecciones que se basan en el comparador predeterminado.
Mejoras en la máquina virtual
- Despacho de interfaces en caché en plataformas no JIT: En plataformas que carecían de soporte JIT, como iOS, el despacho de interfaces recaía en un costoso camino genérico de corrección. El despacho en caché produce hasta 200 mejoras en código con mucha interfaz en estos objetivos.
-
Guid.NewGuid()en Linux:Guid.NewGuid() en Linux ahora utiliza elgetrandom()syscall con caché por lotes en lugar de leer desde/dev/urandom, lo que produce una mejora de aproximadamente un 12% en el rendimiento para la generación de GUID.
Mejoras de WebAssembly
La compatibilidad con Explorador y WebAssembly tiene varias mejoras:
- Carga de carga de WebCIL: El tiempo de ejecución ahora puede cargar cargas de WebCIL directamente, lo que mejora la compatibilidad con escenarios de implementación basados en explorador.
- Símbolos de depuración mejorados: La calidad de los símbolos y del seguimiento de pila para la depuración en WebAssembly ha mejorado, lo que facilita el diagnóstico de problemas en las aplicaciones .NET alojadas en el navegador.
-
float[],Span<float>, yArraySegment<float>marshaling:float[],Span<float>, yArraySegment<float>ahora se organizan más directamente a través de los límites de JavaScript, reduciendo la sobrecarga para código con mucho interop.
.NET incluye mejoras en CoreCLR-on-WebAssembly:
-
WebCIL V1 es el valor predeterminado para las compilaciones WASM de CoreCLR: El encabezado webCIL compartido obtiene un
TableBasecampo (28 → 32 bytes). Los lectores Mono y CoreCLR aceptan V0 y V1.WasmObjectWriterde Crossgen2 produce V1 directamente, y las compilaciones del SDK de WASM basadas en CoreCLR establecen de forma predeterminadaWasmWebcilVersionenV1. -
El re-enlace nativo funciona con las aplicaciones WASM de CoreCLR: Una canalización completa basada en Emscripten sustituye los anteriores destinos ficticios. Volver a enlazar
dotnet.native.wasmdel paquete en tiempo de ejecución e incluir código nativo personalizado a través deNativeFileReferenceahora funciona. - Minificación de JavaScript en compilaciones de versión de lanzamiento: Las compilaciones de versión de lanzamiento de Browser CoreCLR incluyen JavaScript minimizado.
-
La publicación de NativeAOT para WASM ya no omite los ensamblados satélite de los paquetes: Los ensamblados satélite de los paquetes NuGet ahora se pasan a ILC y se excluyen del resultado de la publicación, lo que corrige la localización de las aplicaciones publicadas con AOT que dependen de paquetes como
System.CommandLine.
Compatibilidad de plataforma con más de 1024 CPU
El entorno de ejecución de .NET ahora puede inicializarse en máquinas con más de 1024 procesadores lógicos. Anteriormente, sched_getaffinity se invocaba con el valor por defecto cpu_set_t (limitado a 1024), lo que hacía que fallara la inicialización en servidores con un elevado número de núcleos. El entorno de ejecución ahora asigna dinámicamente el conjunto de procesadores. El GC mantiene su límite de 1024 montones, pero se elimina el límite del número de CPU.