.NET içerisinde Common Language Runtime’ın bir parçası olan JIT compiler‘ın sağladığı özelliklerden olan “constant folding” ve “function folding” kavramlarına baktık bu videoda.
JIT compiler’ı bir dizi içerisinde eğer aynı branch’te (yani farklı bir kod akışına dallanma olmadan) büyük index’li kontrolü yaptıktan sonra kendisinden küçük index’ler için ayrı bir kontrol yapmasına gerek olmayacak şekilde optimizasyon yapıyor.
Yani
arr[2] = 3;
için 2 index’i için bir karşılaştırma yaptığında, bu satırdan daha sonra aynı brach’te;
arr[1] = 3;
şeklinde bir kodunuz varsa, ben zaten 2 için kontrol ettim, 2 index’li bir elemanı olan bir dizinin 1 index’li bir elemanı her zaman vardır şeklinde düşünüp burada bir karşılaştırma yapmayarak optimizasyona gidiyor.
Tabi bu optimizasyon farklı branch’lerde mevcut değil.
.NET içerisinde Common Language Runtime‘ın bir parçası olan JIT compiler‘ın Loop cloning ve Loop Hoisting (Loop-invariant code motion olarak ta geçiyor) konularında yaptıklarına değindik bu videoda.
.NET içerisinde Common Language Runtime‘ın bir parçası olan JIT compiler’ın tam sayı aritmetiği konusunda yaptıklarına değindim.
JIT derleme, interpreted programların performansını iyileştirmeye yönelik bir yöntem. Runtime sırasında, performansını artırmak için program assembly kod’ta derlenebilir. Buna sürece “dinamik derleme” de deniliyor.
Bu kavram ilk olarak John McCarthy’nin 1960 yılında yayınladığı ” Recursive functions of symbolic expressions and their computation by machine, Part I” çalışmasında belirtilmiş: http://jmc.stanford.edu/articles/recursive/recursive.pdf
JIT derlemenin statik derlemeye göre bazı avantajları vardır. C# uygulamalarını çalıştırırken, “runtime” uygulama çalıştırılırken uygulamanın profilini (profiling) çıkarabilir. Bu, daha optimize edilmiş kodun oluşturulmasına izin verir. Uygulama çalışırken davranışı değişirse, runtime bu kodu yeniden derleyebilir.
Dezavantajlardan bazıları, startup gecikmeleri ve runtime sırasında derleme ek yükünü barındırmak. Ek yükü sınırlamak için birçok jit derleyicisi yalnızca sık kullanılan kod kısımlarını derler mesela.
C# içerisinde bir kodun derlenme süreci şu şekilde;
1. Derleme, sırasında kaynak kodunuzu MSIL veya IL (tam özellikli bir dildir bu arada IL fakat C#’a göre biraz daha düşük seviyede) çevirir ve gerekli meta verileri oluşturur.
2. Runtime sırasında, JIT derleyicisi, aldığı IL kodunu assembly koduna çevirir. (Kullandığı runtime ve architecture’a göre üretilen assembly kodu da değişir. Örneğin; x86, x64 veya ARM gibi). Bu nedenle .NET Runtime assembly dosyalarınızın içerisindeki IL kodunu yorumlamaz.
3. CLR, kodun çalıştırılmasını sağlayan altyapıyı ve runtime sırasında kullanılabilecek hizmetleri sağlar. Derlenmiş makine kodu, kod bölümünün bir sonraki çalıştırılışında yeniden kullanılabilecek şekilde bellekte tutulur. Bir işlevi ikinci kez çağırdığınızda, onu ilk çağırdığınızdan daha hızlı çalışacaktır çünkü ikinci kez herhangi bir JIT adımı gerekli değildir.
.NET 6 ile gelen yeniliklerden biri de Windows time zone desteğine ilave olarak IANA time zone desteğinin geldi.
.NET 6 ile birlikte bütün platform’larda Windows time zone desteğine ek olarak IANA time zone desteği geldi. Bildiğiniz gibi günümüzde IANA time zone en güncel ve en popüler zaman dili bilgisine sahip yapılardan biri. Bu nedenle bu desteğin gelmesi geliştiriciler için gayet faydalı oldu.
Aşağıdaki kod parçasına bir göz atalım;
var ianaZoneId = "Europe/Istanbul";
var zoneInfo = TimeZoneInfo.FindSystemTimeZoneById(ianaZoneId);
Console.WriteLine(zoneInfo.DisplayName);
// (UTC+03:00) Istanbul
TimeZoneInfo.TryConvertIanaIdToWindowsId(ianaZoneId, out var windowsId);
Console.WriteLine(windowsId);
// Turkey Standard Time
TimeZoneInfo.TryConvertWindowsIdToIanaId(windowsId, out ianaZoneId);
Console.WriteLine(ianaZoneId);
// Europe/Istanbul
Console.WriteLine(zoneInfo.HasIanaId); // True
.NET 6 ile gelen yeniliklerden biri de LINQ içerisindeki *OrDefault metodlarında default değer atanma özelliği.
.NET 5 ve öncesinde IEnumerable içerisinde bulunan First, Last ve Single metodları aldıkları predicate’e göre herhangi bir eleman bulunamazsa InvalidOperationException fırlatıyorlardı.
Yani;
var list = new List int { 1, 2, 3};
var first = list.First(i => i > 3); // InvalidOperationException
var last = list.Last(i => i > 3); // InvalidOperationException
var single = list.Single(i => i > 3); // InvalidOperationException
.NET 6 ile birlikte bu metodlara ek olarak FirstOrDefault, LastOrDefault ve SingleOrDefault metodlarına default değer atama özelliği geldi. Bunlar aldıkları predicate’e göre eğer bir eleman bulunamazsa defaultValue ile verdiğiniz değeri geri döndürüyorlar.
first = list.FirstOrDefault(i => i > 3, -1);
last = list.LastOrDefault(i => i > 3, -2);
single = list.SingleOrDefault(i => i > 3, -3);
Console.WriteLine($"{first} {last} {single}");
// -1 -2 -3
.NET 6 ile birlikte hayatımıza giren yeniliklerden biri de IEnumerable.Chunk metodu.
.NET 5 ve öncesinde, collection’ları eşit sayıda elemanlar olacak şekilde parçalara ayırmak için 2 yöntem vardı.
Birincisi aşağıdaki gibi bir custom (veya extension) metod yazmak;
static List List T Split T (IList T source, int size)
{
return source.Select((x, i) = new { Index = i, Value = x })
.GroupBy(x => x.Index / size)
.Select(x => x.Select(v = v.Value).ToList())
.ToList();
}
İkincisi de MoreLINQ içerisinde bulunan Batch metodunu kullanmaktı.
var buckets = numbers.Batch(10);
.NET 6 ile gelen IEnumerable.Chunk metodu ile bu işlemi artık custom bir metoda veya bir nuget paketine ihtiyaç duymadan halledebiliyoruz.
IEnumerable int numbers = Enumerable.Range(1, 34);
IEnumerable int[] buckets = numbers.Chunk(10);
foreach (int[] bucket in buckets)
{
Console.WriteLine($"{bucket.First()} {bucket.Last()}");
}