Mise à jour des exigences matérielles

Le runtime .NET 11 passe du niveau d’instruction x86-64-v1 à x86-64-v2 sur les plateformes Apple, Linux et Windows. Le nouveau baseline inclut les jeux d’instructions CX16, POPCNT, SSE3, SSSE3, SSE4.1 et SSE4.2, en plus de CMOV, CX8, SSE et SSE2. Cette évolution suit les exigences de Windows 11 et les processeurs Intel ou AMD supportés depuis 2013. Pour les cibles ReadyToRun, Windows et Linux utilisent désormais x86-64-v3, ajoutant AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT et MOVBE, tandis que la cible Apple reste inchangée.

Sur Arm64, la version Apple conserve la base armv8.5‑a, couvrant NEON, CRC, DOTPROD, LSE, RCPC, RCPC2 et RDMA. Sous Linux, le ReadyToRun cible intègre LSE, ce qui peut augmenter le temps de jitting sur certains appareils. Sous Windows, le baseline passe à LSE, aligné avec les exigences SBSA du serveur, et le ReadyToRun cible devient armv8.2‑a + RCPC, incluant AdvSimd, CRC, LSE, RCPC et RDMA. Le runtime refuse de s’exécuter sur du matériel ne respectant pas ces jeux d’instructions, affichant un message d’erreur explicite.

Nouveautés de l’exécution asynchrone

.NET 11 propose le runtime‑async V2, qui remplace les machines d’état générées par le compilateur par une gestion native du suspend‑resume. L’activation se fait via le fichier projet :

<PropertyGroup>
  <Features>runtime-async=on</Features>
</PropertyGroup>

Le runtime ne nécessite plus la balise <EnablePreviewFeatures>true</EnablePreviewFeatures> pour cette fonctionnalité. Les bibliothèques du runtime sont déjà compilées avec runtime-async=on, ce qui permet de migrer une application complète sans modifier le code source. Le modèle réduit la profondeur de la pile d’appels et améliore la lisibilité des traces.

Traces de pile simplifiées

Avec le runtime‑async, chaque appel asynchrone apparaît directement dans la pile, contrairement aux 13 cadres générés par la machine d’état traditionnelle. Exemple de code :

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));
}

Sans runtime‑async, la trace montre les méthodes internes de la machine d’état (<Main>$>g__InnerAsync|0_2, etc.), alors qu’avec la nouvelle implémentation les seules entrées sont OuterAsync, MiddleAsync et InnerAsync.

Améliorations du compilateur et de l’inlining

Le runtime introduit deux correctifs : les surcharges covariantes Task → Task<T> génèrent désormais un thunk void qui préserve l’appel virtuel, et les restrictions d’inlining pour les méthodes async pendant la compilation ReadyToRun ont été levées. Ainsi, les appels async sans await peuvent être inlinés, ce qui se vérifie à la fois avec crossgen2 et la compilation composite R2R. Les variables d’environnement DOTNET_RuntimeAsync et UNSUPPORTED_RuntimeAsync ont été supprimées ; le désactivation se fait maintenant via <UseRuntimeAsync>false</UseRuntimeAsync> dans le projet.