Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los 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 3.
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>
A partir de la versión preliminar 3, un net11.0 proyecto ya no requiere <EnablePreviewFeatures>true</EnablePreviewFeatures> usar Async en tiempo de ejecución.
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
La versión preliminar 3 agrega compatibilidad asincrónica en tiempo de ejecución para 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.
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 versión preliminar 3 amplía la compatibilidad con el explorador y WebAssembly con 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.