Aracılığıyla paylaş


.NET 9 çalışma zamanındaki yenilikler

Bu makalede.NET 9 için .NET çalışma zamanındaki yeni özellikler ve performans geliştirmeleri açıklanmaktadır.

Kırpma desteğine sahip özellik anahtarları için öznitelik modeli

İki yeni öznitelik, .NET kitaplıklarının (ve sizin) işlev alanlarını değiştirmek için kullanabileceğiniz özellik anahtarlarını tanımlamayı mümkün hale getirir. Bir özellik desteklenmiyorsa, Yerel AOT ile kırpma veya derleme sırasında desteklenmeyen (ve dolayısıyla kullanılmayan) özellikler kaldırılır ve bu da uygulama boyutunu daha küçük tutar.

  • FeatureSwitchDefinitionAttribute , kırpma sırasında bir özellik anahtarı özelliğini sabit olarak işlemek için kullanılır ve anahtar tarafından korunan geçersiz kod kaldırılabilir:

    if (Feature.IsSupported)
        Feature.Implementation();
    
    public class Feature
    {
        [FeatureSwitchDefinition("Feature.IsSupported")]
        internal static bool IsSupported => AppContext.TryGetSwitch("Feature.IsSupported", out bool isEnabled) ? isEnabled : true;
    
        internal static void Implementation() => ...;
    }
    

    Uygulama proje dosyasında aşağıdaki özellik ayarlarıyla kırpıldığında, Feature.IsSupported olarak falsekabul edilir ve Feature.Implementation kod kaldırılır.

    <ItemGroup>
      <RuntimeHostConfigurationOption Include="Feature.IsSupported" Value="false" Trim="true" />
    </ItemGroup>
    
  • FeatureGuardAttribute, veya RequiresUnreferencedCodeAttributeile RequiresAssemblyFilesAttributeRequiresDynamicCodeAttributeek açıklama eklenen kod için bir özellik anahtarı özelliğini bir koruyucu olarak işlemek için kullanılır. Örneğin:

    if (Feature.IsSupported)
        Feature.Implementation();
    
    public class Feature
    {
        [FeatureGuard(typeof(RequiresDynamicCodeAttribute))]
        internal static bool IsSupported => RuntimeFeature.IsDynamicCodeSupported;
    
        [RequiresDynamicCode("Feature requires dynamic code support.")]
        internal static void Implementation() => ...; // Uses dynamic code
    }
    

    ile <PublishAot>true</PublishAot>Feature.Implementation() oluşturulduğunda çağrısı çözümleyici uyarısı IL3050 üretmez ve Feature.Implementation yayımlarken kod kaldırılır.

UnsafeAccessorAttribute genel parametreleri destekler

Bu özellik, UnsafeAccessorAttribute çağıranın erişemeyeceği tür üyelerine güvenli olmayan erişim sağlar. Bu özellik .NET 8'de tasarlanmıştır ancak genel parametreler için destek olmadan uygulanır. .NET 9, CoreCLR ve yerel AOT senaryoları için genel parametreler için destek ekler. Aşağıdaki kod örnek kullanımı gösterir.

using System.Runtime.CompilerServices;

public class Class<T>
{
    private T? _field;
    private void M<U>(T t, U u) { }
}

class Accessors<V>
{
    [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_field")]
    public extern static ref V GetSetPrivateField(Class<V> c);

    [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "M")]
    public extern static void CallM<W>(Class<V> c, V v, W w);
}

internal class UnsafeAccessorExample
{
    public void AccessGenericType(Class<int> c)
    {
        ref int f = ref Accessors<int>.GetSetPrivateField(c);

        Accessors<int>.CallM<string>(c, 1, string.Empty);
    }
}

Çöp toplama

Uygulama boyutlarına (DATAS) dinamik uyarlama artık varsayılan olarak etkindir. Uygulama belleği gereksinimlerine uyum sağlamayı amaçlar; bu da uygulama yığını boyutunun kabaca uzun ömürlü veri boyutuyla orantılı olması gerektiği anlamına gelir. DATAS, .NET 8'de bir katılım özelliği olarak sunulmuştur ve .NET 9'da önemli ölçüde güncelleştirilmiş ve geliştirilmiştir.

Daha fazla bilgi için bkz . Uygulama boyutlarına dinamik uyarlama (DATAS).

Denetim akışı zorlama teknolojisi

Denetim akışı zorlama teknolojisi (CET), Windows'ta uygulamalar için varsayılan olarak etkindir . Dönüş odaklı programlama (ROP) açıklarına karşı donanım tarafından zorlanan yığın koruması ekleyerek güvenliği önemli ölçüde artırır. En son .NET Çalışma Zamanı Güvenlik Azaltma.

CET, CET özellikli işlemlere bazı sınırlamalar uygular ve küçük bir performans gerilemesi ile sonuçlanabilir. CET'i geri çevirmek için çeşitli denetimler vardır.

.NET yükleme arama davranışı

.NET uygulamaları artık .NET çalışma zamanını nasıl arayabilecekleri için yapılandırılabilir. Bu özellik, özel çalışma zamanı yüklemeleriyle veya yürütme ortamını daha güçlü bir şekilde denetlemek için kullanılabilir.

Performans iyileştirmeleri

.NET 9 için aşağıdaki performans geliştirmeleri yapılmıştır:

Döngü iyileştirmeleri

Döngüler için kod oluşturmanın geliştirilmesi .NET 9 için bir önceliktir. Aşağıdaki geliştirmeler kullanıma sunuldu:

Uyarı

İndüksiyon-değişken genişletme ve dizinleme sonrası adresleme benzerdir: her ikisi de döngü dizini değişkenleriyle bellek erişimlerini iyileştirir. Ancak Arm64 bir CPU özelliği sunduğundan ve x64 özelliği sunmadığından farklı yaklaşımlar benimserler. CPU/ISA özelliği ve gereksinimlerindeki farklılıklar nedeniyle x64 için indüksiyon değişken genişletmesi uygulandı.

indüksiyon-değişken genişletme

64 bit derleyici , indüksiyon değişkeni (IV) genişletme olarak adlandırılan yeni bir iyileştirme içerir.

IV, içeren döngü yinelenirken değeri değişen bir değişkendir. Aşağıdaki for döngüde iv ifor (int i = 0; i < 10; i++): . Derleyici bir IV değerinin döngüsünün yinelemeleri üzerinde nasıl geliştiğini analiz edebilirse, ilgili ifadeler için daha yüksek performanslı kod üretebilir.

Bir dizide yineleyen aşağıdaki örneği göz önünde bulundurun:

static int Sum(int[] nums)
{
    int sum = 0;
    for (int i = 0; i < nums.Length; i++)
    {
        sum += nums[i];
    }

    return sum;
}

dizin değişkeninin iboyutu 4 bayttır. Derleme düzeyinde, 64 bit yazmaçlar genellikle x64'teki dizi dizinlerini tutmak için kullanılır ve önceki .NET sürümlerinde, derleyici tarafından oluşturulan kod dizi erişimi için 8 bayt'a kadar uzatılır i , ancak başka bir yerde 4 baytlık tamsayı olarak değerlendirilmeye i devam eder. Ancak, 8 bayta kadar uzatmak i için x64 üzerinde ek yönerge gerekir. IV genişletme ile 64 bit JIT derleyicisi artık döngü boyunca 8 bayta genişletilir i ve sıfır uzantıyı atlar. Diziler üzerinde döngü yapmak çok yaygındır ve bu yönerge kaldırma işleminin avantajları hızla eklenir.

Arm64'te dizine ekleme sonrası adresleme

Dizin değişkenleri genellikle belleğin sıralı bölgelerini okumak için kullanılır. İdiomatic for döngüsünü göz önünde bulundurun:

static int Sum(int[] nums)
{
    int sum = 0;
    for (int i = 0; i < nums.Length; i++)
    {
        sum += nums[i];
    }

    return sum;
}

Döngünün her yinelemesi için dizin değişkeni i içindeki bir tamsayıyı numsokumak için kullanılır ve ardından i artırılır. Arm64 derlemesinde bu iki işlem aşağıdaki gibi görünür:

ldr w0, [x1]
add x1, x1, #4

ldr w0, [x1] içindeki bellek adresindeki x1 tamsayıyı içine w0yükler; bu, kaynak kodundaki erişimine nums[i] karşılık gelir. Ardından, add x1, x1, #4 içindeki adresi x1 dört bayt (tamsayı boyutu) artırarak içindeki numsbir sonraki tamsayıya geçin. Bu yönerge, her yinelemenin i++ sonunda yürütülen işleme karşılık gelir.

Arm64, "dizin" yazmacının adresi kullanıldıktan sonra otomatik olarak artırıldığı dizinli adresledikten sonra adresle oluşturmayı destekler. Bu, iki yönergenin tek bir yönergede birleştirilerek döngünün daha verimli hale getirilebileceği anlamına gelir. CPU'nun iki yönerge yerine yalnızca bir yönergenin kodunu çözmesi gerekir ve döngünün kodu artık daha önbellek kullanımına uygundur.

Güncelleştirilmiş derleme şöyle görünür:

ldr w0, [x1], #0x04

#0x04 sonundaki değeri, içine x1 bir tamsayı w0yüklemek için kullanıldıktan sonra içindeki adresin dört bayt artırıldığında anlamına gelir. 64 bit derleyicisi artık Arm64 kodu oluştururken dizine alınan son adresleme kullanıyor.

Güç azaltma

Güç azaltma, bir işlemin daha hızlı, mantıksal olarak eşdeğer bir işlemle değiştirildiği bir derleyici iyileştirmesidir. Bu teknik özellikle döngüleri iyileştirmek için kullanışlıdır. İdiomatic for döngüsünü göz önünde bulundurun:

static int Sum(int[] nums)
{
    int sum = 0;
    for (int i = 0; i < nums.Length; i++)
    {
        sum += nums[i];
    }

    return sum;
}

Aşağıdaki x64 derleme kodu, döngünün gövdesi için oluşturulan kodun bir parçacığını gösterir:

add ecx, dword ptr [rax+4*rdx+0x10]
inc edx

Bu yönergeler sırasıyla ve sum += nums[i]ifadelerine i++ karşılık gelir. rcx(ecx bu yazmaç değerinin daha düşük 32 bitini tutar) değerini sumrax içerir, temel adresini numsiçerir ve rdx değerini iiçerir. adresini nums[i]hesaplamak için içindeki rdx dizin dört ile çarpılır (tamsayı boyutu). Bu uzaklık daha sonra içindeki temel adrese rax. (konumundaki nums[i] tamsayı okunduktan sonra öğesine eklenir rcx ve içindeki rdx dizin artırılır.) Başka bir deyişle, her dizi erişimi bir çarpma ve bir toplama işlemi gerektirir.

Çarpma, toplamadan daha pahalıdır ve eskisini ikincisiyle değiştirmek, güç azaltma için klasik bir motivasyondur. Her bellek erişiminde öğenin adresinin hesaplanmasından kaçınmak için, dizin değişkeni yerine işaretçi kullanarak içindeki nums tamsayılara erişmek için örneği yeniden yazabilirsiniz:

static int Sum2(Span<int> nums)
{
    int sum = 0;
    ref int p = ref MemoryMarshal.GetReference(nums);
    ref int end = ref Unsafe.Add(ref p, nums.Length);
    while (Unsafe.IsAddressLessThan(ref p, ref end))
    {
        sum += p;
        p = ref Unsafe.Add(ref p, 1);
    }

    return sum;
}

Kaynak kod daha karmaşıktır, ancak mantıksal olarak ilk uygulamaya eşdeğerdir. Ayrıca, derleme daha iyi görünür:

add ecx, dword ptr [rdx]
add rdx, 4

rcx (ecx bu yazmacın daha düşük 32 bitini barındırıyor) değerini sumhala barındırıyor, ancak rdx artık tarafından pişaret edilen adresi barındırıyor, bu nedenle içindeki nums öğelere erişmek için başvurumuzu kaldırmamız rdxgerekiyor. İlk örnekteki tüm çarpma ve ekleme, işaretçiyi ileriye taşımak için tek add bir yönergeyle değiştirilmiştir.

.NET 9'da JIT derleyicisi, herhangi bir kodu yeniden yazmanıza gerek kalmadan ilk dizin oluşturma desenini otomatik olarak ikinciye dönüştürür.

Döngü sayacı değişken yönü

64 bit derleyici artık bir döngünün sayaç değişkeninin yalnızca yineleme sayısını denetlemek için kullanıldığını algılar ve döngünün yukarı değil, geri sayım için dönüşümünü sağlar.

İdiomatik for (int i = ...) desende, sayaç değişkeni genellikle artar. Aşağıdaki örneği inceleyin:

for (int i = 0; i < 100; i++)
{
    DoSomething();
}

Ancak birçok mimaride, döngünün sayacını azaltma performansı daha yüksektir, örneğin:

for (int i = 100; i > 0; i--)
{
    DoSomething();
}

İlk örnekte, derleyicinin değerini artırmaya iyönelik bir yönerge ve ardından karşılaştırmayı gerçekleştirme i < 100 yönergesi ve ardından koşul hala truedevam ediyorsa döngüye devam etmek için koşullu atlama (toplamda üç yönerge) göndermesi gerekir. Ancak, sayacın yönü çevrilmişse, bir daha az yönerge gerekir. Örneğin, x64'te derleyici azaltma yönergesini decikullanabilir; i sıfıra ulaştığında yönerge, dec hemen sonrasında decbir atlama yönergesi için koşul olarak kullanılabilecek bir CPU bayrağı ayarlar.

Kod boyutunu küçültme küçüktür, ancak döngü önemsiz sayıda yineleme için çalıştırılırsa performans artışı önemli olabilir.

Inlining geliştirmeleri

bunlardan biri. NET'in JIT derleyicisinin inliner'ı için hedefleri, bir yöntemin mümkün olduğunca inlined olmasını engelleyen birçok kısıtlamayı kaldırmaktır. .NET 9, şunların çizilmesine olanak tanır:

  • Çalışma zamanı aramaları gerektiren paylaşılan jenerikler.

    Örneğin, aşağıdaki yöntemleri göz önünde bulundurun:

    static bool Test<T>() => Callee<T>();
    static bool Callee<T>() => typeof(T) == typeof(int);
    

    gibi bir başvuru türü olduğunda T çalışma zamanı, özel örneklemeleri string olan ve tüm başvuru türü Test türleri tarafından paylaşılan paylaşılan Callee türler oluşturur.T Bunu yapmak için çalışma zamanı, genel türleri iç türlerle eşleyen sözlükler oluşturur. Bu sözlükler, genel tür başına (veya genel yöntem başına) özelleştirilmiştir ve çalışma zamanında T'ye bağlı olan türler ve T hakkında bilgi almak için erişilir. Geçmişte, tam zamanında derlenen kod yalnızca kök yöntemin sözlüğünde bu çalışma zamanı aramalarını gerçekleştirebilirdi. Bu, JIT derleyicisinin içine satır içi CalleeTestolarak giremediği anlamına geliyordu; her iki yöntem de aynı tür üzerinde örneklenmiş olsa bile, satır içi kodun Callee doğru sözlüğe erişmesinin hiçbir yolu yoktu.

    .NET 9, çağıranlarda çalışma zamanı türü aramalarını serbestçe etkinleştirerek bu kısıtlamayı kaldırdı; yani JIT derleyicisi artık gibi Callee satır içi yöntemleri içine Testalabilir.

    Başka bir yöntem çağıracağımızı Test<string> varsayalım. Sahte kodda, inlining şu şekilde görünür:

    static bool Test<string>() => typeof(string) == typeof(int);
    

    Bu tür denetimi derleme sırasında hesaplanabilir, bu nedenle son kod şöyle görünür:

    static bool Test<string>() => false;
    

    JIT derleyicisinin inliner'ında yapılan iyileştirmeler, diğer iç kararlar üzerinde bileşik etkilere neden olabilir ve bu da önemli performans kazançlarına neden olabilir. Örneğin, satır içi Callee karar, çağrının da satır içine alınmasına ve bu şekilde devam edilmesine Test<string> olanak sağlayabilir. Bu, en az 80 kıyaslama ile 10% veya daha fazla iyileştirilmiş yüzlerce kıyaslama iyileştirmesi üretmektedir.

  • Windows x64, Linux x64 ve Linux Arm64'te iş parçacığı yerel statiklerine erişir.

    Sınıf üyeleri için static , üyenin tam olarak bir örneği, üyeyi "paylaşan" sınıfın tüm örnekleri arasında bulunur. Bir static üyenin değeri her iş parçacığı için benzersizse, bu değerin iş parçacığı yerel hale getirilmesi, bir eşzamanlılık temel öğesinin üyeye içeren iş parçacığından güvenli bir şekilde erişme static gereksinimini ortadan kaldırdığı için performansı geliştirebilir.

    Daha önce, Yerel AOT ile derlenmiş programlarda iş parçacığı yerel statiklerine erişimler, derleyicinin iş parçacığı yerel depolamasının temel adresini almak için çalışma zamanına bir çağrı yaymasına ihtiyaç duymaktadır. Artık derleyici bu çağrıları satır içinde satır içi yapabilir ve bu verilere erişmek için çok daha az yönergeyle sonuçlanabilir.

PGO geliştirmeleri: Tür denetimleri ve atamaları

.NET 8 varsayılan olarak dinamik profil destekli iyileştirmeyi (PGO) etkinleştirdi. NET 9, JIT derleyicisinin PGO uygulamasını genişleterek daha fazla kod deseninin profilini oluşturur. Katmanlı derleme etkinleştirildiğinde, JIT derleyicisi davranışının profilini almak için programınıza izleme ekler. İyileştirmelerle yeniden derlendiğinde derleyici, programınızın geçerli çalıştırmasına özgü kararlar almak için çalışma zamanında oluşturduğu profilden yararlanıyor. .NET 9'da JIT derleyicisi , tür denetimlerinin performansını artırmak için PGO verilerini kullanır.

Bir nesnenin türünü belirlemek için çalışma zamanına bir çağrı yapılması gerekir ve bu da bir performans cezasıyla birlikte gelir. Bir nesnenin türünün denetlenmesi gerektiğinde, JIT derleyicisi bu çağrıyı doğruluk açısından yayar (derleyiciler genellikle olasılıksız görünseler bile olasılıkları eleyemez). Ancak PGO verileri bir nesnenin belirli bir tür olduğunu gösteriyorsa, JIT derleyicisi artık bu türü ucuz bir şekilde denetleyebilen hızlı bir yol yayar ve yalnızca gerekirse çalışma zamanına çağrının yavaş yoluna geri döner.

.NET kitaplıklarında Arm64 vektörleştirmesi

Yeni EncodeToUtf8 bir uygulama, JIT derleyicisinin Arm64'te çoklu yazmaç yükleme/depolama yönergeleri yayma özelliğinden yararlanır. Bu davranış, programların daha az yönergeyle daha büyük veri öbeklerini işlemesine olanak tanır. Çeşitli etki alanlarındaki .NET uygulamaları, arm64 donanımında bu özellikleri destekleyen aktarım hızı iyileştirmelerini görmelidir. Bazı kıyaslamalar yürütme sürelerini yarıdan fazla azaltıyor.

Arm64 kod oluşturma

JIT derleyicisi, Arm64'te yönergeyi (yükleme değerleri için) kullanmak ldp üzere bitişik yüklerin gösterimini dönüştürme özelliğine zaten sahiptir. .NET 9, işlemleri depolamak için bu özelliği genişletir.

Yönerge, str tek bir yazmaçtan belleğe veri depolarken stp yönerge bir kayıt çiftinden verileri depolar. stp Yerine str kullanmak, aynı görevin daha az depolama işlemiyle gerçekleştirilebileceği anlamına gelir ve bu da yürütme süresini artırır. Tek bir yönergeyi tıraş etmek küçük bir gelişme gibi görünebilir, ancak kod, önemsiz sayıda yineleme için döngüde çalışıyorsa, performans artışları hızlı bir şekilde eklenebilir.

Örneğin, aşağıdaki kod parçacığını göz önünde bulundurun:

class Body { public double x, y, z, vx, vy, vz, mass; }

static void Advance(double dt, Body[] bodies)
{
    foreach (Body b in bodies)
    {
        b.x += dt * b.vx;
        b.y += dt * b.vy;
        b.z += dt * b.vz;
    }
}

, b.xve b.y değerleri b.zdöngü gövdesinde güncelleştirilir. Derleme düzeyinde, her üye bir str yönergeyle depolanabilir; veya kullanılarak stpdepolardan ikisi (b.x ve b.yveya b.y ve b.z, çünkü bu çiftler bellekte bitişiktir) tek bir yönergeyle işlenebilir. komutunu kullanarak stp ve b.xb.y aynı anda depolamak için derleyicinin ayrıca hesaplamaların b.x + (dt * b.vx) birbirinden bağımsız olduğunu ve b.y + (dt * b.vy) ile b.xdepolamadan önce b.y gerçekleştirilebileceğini belirlemesi gerekir.

Daha hızlı özel durumlar

CoreCLR çalışma zamanı, özel durum işleme performansını geliştiren yeni bir özel durum işleme yaklaşımı benimsemiştir. Yeni uygulama, NativeAOT çalışma zamanının özel durum işleme modelini temel alır. Değişiklik, Windows yapılandırılmış özel durum işleme (SEH) ve Unix üzerindeki öykünmesi desteğini kaldırır. Yeni yaklaşım, Windows x86 (32 bit) dışında tüm ortamlarda desteklenir.

Yeni özel durum işleme uygulaması, mikro karşılaştırmaları işleyen bazı özel durumlar için 2-4 kat daha hızlıdır. Performans laboratuvarında aşağıdaki performans geliştirmeleri ölçülmüştür:

Yeni uygulama varsayılan olarak etkindir. Ancak, eski özel durum işleme davranışına geri dönmeniz gerekirse, bunu aşağıdaki yollardan biriyle yapabilirsiniz:

  • dosyasında olarak System.Runtime.LegacyExceptionHandlingtrueayarlayınruntimeconfig.json.
  • Ortam değişkenini DOTNET_LegacyExceptionHandling olarak 1ayarlayın.

Kod düzeni

Derleyiciler genellikle her bloğun yalnızca ilk yönergede girilebilen ve son yönergeyle çıkılan bir kod öbekleri olduğu temel blokları kullanarak bir programın denetim akışına neden olur. Temel blokların sırası önemlidir. Bir blok dal yönergesiyle biterse denetim akışı başka bir bloğa aktarılır. Blok yeniden sıralamanın bir hedefi, oluşturulan koddaki dal yönergelerinin sayısını azaltmak ve hata davranışını en üst düzeye çıkarmaktır. Her temel bloğu en olası ardılı takip ederse, atlama gerekmeden ardılına "düşebilir".

Yakın zamana kadar, JIT derleyicisindeki blok yeniden sıralama akışı uygulamasıyla sınırlıydı. .NET 9'da JIT derleyicisinin blok yeniden sıralama algoritması daha basit ve daha genel bir yaklaşımla değiştirilmiştir. Akış çizelgesi veri yapıları şu şekilde yeniden düzenlenmiş:

  • Blok sıralamayla ilgili bazı kısıtlamaları kaldırın.
  • Bloklar arasındaki her denetim akışı değişikliğine ingrain yürütme olasılıkları.

Ayrıca, yöntemin akış grafiği dönüştürüldükçe profil verileri yayılır ve korunur.

Adres açığa çıkarma azaltıldı

.NET 9'da, JIT derleyicisi yerel değişken adreslerinin kullanımını daha iyi izleyebilir ve gereksiz adres açıklarından kaçınabilir.

Yerel değişkenin adresi kullanıldığında, JIT derleyicisi yöntemi iyileştirirken ek önlemler almalıdır. Örneğin, derleyicinin bir çağrıdaki yerel değişkenin adresini başka bir yönteme geçiren bir yöntemi iyileştirdiğini varsayalım. Çağıran yerel değişkene erişmek için adresi kullandığından, doğruluğu korumak için derleyici değişkenin dönüştürülmesini önler. Ele alınan yerel ayarlar, derleyicinin iyileştirme potansiyelini önemli ölçüde engelleyebilir.

AVX10v1 desteği

Intel'den yeni bir SIMD yönerge kümesi olan AVX10 için yeni API'ler eklendi. Yeni Avx10v1 API'leri kullanarak vektörleştirilmiş işlemlerle AVX10 özellikli donanımlarda .NET uygulamalarınızı hızlandırabilirsiniz.

Donanım iç kod oluşturma

Birçok donanım iç API'si, kullanıcıların belirli parametreler için sabit değerler geçirmesini bekler. Bu sabitler, yazmaçlara yüklenmek veya bellekten erişmek yerine doğrudan iç yönergesine kodlanır. Sabit sağlanmazsa, iç değeri işlevsel olarak eşdeğer ancak daha yavaş bir geri dönüş uygulaması çağrısıyla değiştirilir.

Aşağıdaki örneği inceleyin:

static byte Test1()
{
    Vector128<byte> v = Vector128<byte>.Zero;
    const byte size = 1;
    v = Sse2.ShiftRightLogical128BitLane(v, size);
    return Sse41.Extract(v, 0);
}

çağrısının size kullanımı Sse2.ShiftRightLogical128BitLane 1 sabitiyle değiştirilebilir ve normal koşullarda JIT derleyicisi bu değiştirme iyileştirmesini zaten yapabilir. Ancak için hızlandırılmış veya geri dönüş kodunun Sse2.ShiftRightLogical128BitLaneoluşturulup oluşturulmayacağını belirlerken, derleyici bir değişkenin sabit yerine geçirildiğini algılar ve çağrıyı "içermeye" karşı erken karar verir. .NET 9'dan başlayarak, derleyici bunun gibi daha fazla servis talebi tanır ve değişken bağımsız değişkenini sabit değeriyle değiştirerek hızlandırılmış kodu oluşturur.

Kayan nokta ve SIMD işlemleri için sabit katlama

Sabit katlama , JIT derleyicisinde mevcut bir iyileştirmedir. Sürekli katlama , derleme zamanında hesaplanabilir ifadelerin değerlendirildikleri sabitlerle değiştirilmesini ifade eder ve böylece çalışma zamanında hesaplamaları ortadan kaldırır. .NET 9, yeni sabit katlama özellikleri ekler:

  • İşlenenlerden birinin sabit olduğu kayan nokta ikili işlemleri için:
    • x + NaN şimdi içine NaNkatlanır.
    • x * 1.0 şimdi içine xkatlanır.
    • x + -0 şimdi içine xkatlanır.
  • Donanım iç bilgileri için. Örneğin, bir xolduğunu varsayarsakVector<T>:
    • x + Vector<T>.Zero şimdi içine xkatlanır.
    • x & Vector<T>.Zero şimdi içine Vector<T>.Zerokatlanır.
    • x & Vector<T>.AllBitsSet şimdi içine xkatlanır.

Arm64 SVE desteği

.NET 9, ARM64 CPU'ları için bir SIMD yönerge kümesi olan Ölçeklenebilir Vektör Uzantısı (SVE) için deneysel destek sağlar. .NET, NEON yönerge kümesini zaten desteklediğinden, NEON özellikli donanımlarda uygulamalarınız 128 bit vektör yazmaçlarından yararlanabilir. SVE, 2048 bit'e kadar esnek vektör uzunluklarını destekler ve yönerge başına daha fazla veri işlemenin kilidini açar. .NET 9'da SVE Vector<T> hedeflenirken 128 bit genişliğindedir ve gelecekteki çalışmalar, hedef makinenin vektör yazmaç boyutuyla eşleşecek şekilde genişliğinin ölçeklendirilebilmesini sağlayacaktır. Yeni System.Runtime.Intrinsics.Arm.Sve API'leri kullanarak SVE özellikli donanımlarda .NET uygulamalarınızı hızlandırabilirsiniz.

Uyarı

.NET 9'da SVE desteği deneyseldir. altındaki System.Runtime.Intrinsics.Arm.Sve API'ler ile ExperimentalAttributeişaretlenir, yani gelecek sürümlerde değiştirilebilir. Ayrıca, SVE tarafından oluşturulan kod aracılığıyla hata ayıklayıcı adımlama ve kesme noktaları düzgün çalışmayabilir ve uygulama kilitlenmelerine veya veri bozulmasına neden olabilir.

Kutular için nesne yığını ayırma

ve intgibi struct değer türleri genellikle yığın yerine yığında ayrılır. Ancak, çeşitli kod desenlerini etkinleştirmek için bunlar genellikle nesnelere "kutulanır".

Aşağıdaki kod parçacığını göz önünde bulundurun:

static bool Compare(object? x, object? y)
{
    if ((x == null) || (y == null))
    {
        return x == y;
    }

    return x.Equals(y);
}

public static int RunIt()
{
    bool result = Compare(3, 4);
    return result ? 0 : 100;
}

Compare , dizeler veya double değerler gibi diğer türleri karşılaştırmak isterseniz aynı uygulamayı yeniden kullanabileceğiniz şekilde yazılabilir. Ancak bu örnekte, ona geçirilen değer türlerinin kutulanması için gerekli performans dezavantajı da vardır.

için RunIt oluşturulan x64 derleme kodu aşağıdaki gibidir:

push     rbx
sub      rsp, 32
mov      rcx, 0x7FFB9F8074D0      ; System.Int32
call     CORINFO_HELP_NEWSFAST
mov      rbx, rax
mov      dword ptr [rbx+0x08], 3
mov      rcx, 0x7FFB9F8074D0      ; System.Int32
call     CORINFO_HELP_NEWSFAST
mov      dword ptr [rax+0x08], 4
add      rbx, 8
mov      ecx, dword ptr [rbx]
cmp      ecx, dword ptr [rax+0x08]
sete     al
movzx    rax, al
xor      ecx, ecx
mov      edx, 100
test     eax, eax
mov      eax, edx
cmovne   eax, ecx
add      rsp, 32
pop      rbx
ret

çağrısı CORINFO_HELP_NEWSFAST , kutulanmış tamsayı bağımsız değişkenleri için yığın ayırmalarıdır. Ayrıca, için herhangi bir çağrı Compareolmadığına dikkat edin; derleyici bunu içine RunItsatır içine almak için karar verdi. Bu çizgi, kutuların hiçbir zaman "kaçamayacağı" anlamına gelir. Başka bir deyişle, yürütmesi Compareboyunca tamsayıları bilir x ve y aslında tamsayılardır ve karşılaştırma mantığını etkilemeden bunları güvenli bir şekilde kaldırabilirsiniz.

.NET 9'dan başlayarak, 64 bit derleyici yığında ayrılmamış kutular ayırır ve bu da diğer birçok iyileştirmenin kilidini açar. Bu örnekte, derleyici artık yığın ayırmalarını atlar, ancak x 3 ve y 4 olduğundan, gövdesini Comparede atlayabilir; derleyici derleme zamanında false olduğunu belirleyebilir x.Equals(y) , bu nedenle RunIt her zaman 100 döndürmelidir. Güncelleştirilmiş derleme aşağıdadır:

mov      eax, 100
ret