Category Archives: .NET

JIT Derleyicisine Giriş – Constant folding ve Function folding

.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.

Kaynaklar:

https://en.wikipedia.org/wiki/Constant_folding

https://en.wikipedia.org/wiki/Fold_(higher-order_function)

https://sharplab.io/

https://godbolt.org/

Minimal API | Route Handler için Response çeşitleri

Minimal API içerisinde bir route handler’dan döndürülebilecek response çeşitlerini ele aldık bu videoda.

✅Github reposu: https://github.com/sonergonul/MinimalAPI

Response çeşitleri şu şekilde;

➡️ IResult – Task<IResult> veya ValueTask<IResult>

➡️ string – Task<string> veya ValueTask<string> – text/plain

➡️ T – Json seralize – application/json

🟢 app.MapGet("/response1", () lambda "Hello World");

🟢 app.MapGet("/response2", () => new { Message = "Hello World" });

🟢 app.MapGet("/response3", () => Results.Ok(new { Message = "Hello World" }));

🟢 app.MapGet("/response4", () => Results.Json(new { Message = "Hello World" }));

🟢 app.MapGet("/405", () => Results.StatusCode(405));

🟢 app.MapGet("/text", () => Results.Text("This is some text"));

🟢 app.MapGet("/old-path", () => Results.Redirect("/new-path"));

🟢 app.MapGet("/download", () => Results.File("C:/appsettings.json", contentType: "application/json", fileDownloadName: "appsettings.json"));

JIT Derleyicisine Giriş – Dizilerde sınır kontrolü

.NET içerisinde Common Language Runtime‘ın bir parçası olan JIT compiler‘ın dizilerin index sınırları konusunda yaptığı optimizasyonlara 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.

Videodaki kod bloğu;

 public void M(int[] arr) 
    {
        arr[0] = 1;
        arr[1] = 2;
        arr[2] = 3;
    }
    
    public void M1(int[] arr) 
    {
        arr[2] = 1;
        arr[1] = 2;
        arr[0] = 3;
    }

    public void M(int[] arr, int i) 
    {
        if(i == 1)
        {
            arr[0] = 1;
            arr[1] = 1;
        }
        else if(i == 2)
        {
            arr[0] = 1;
        }
    }

Kaynaklar:

https://sharplab.io

https://github.com/dotnet/coreclr/blob/master/src/jit/rangecheck.cpp

https://en.wikipedia.org/wiki/Branch_predictor

JIT Derleyicisine Giriş – Loop Cloning ve Loop Hoisting

.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.

Kaynaklar:

https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA+ABATARgLABQGAzAATakDCphA3oaY6QPTOkAyEEADlQDYQAdgEtBAc1IA3GBy68AEhGEBnAC6ixDJiXIpSAWRwAKUaoDaAXVIBDKFAA0pUzcfPgAShoEmpWj59a/qQAZtAmgqpOpAC8pAAMANxRADykOHGJTgDUWe6B/vTeQf62UGbCVrHWpFmkwAn5PgC++S1FjPmssjz8QhqkgHqAtqp8toCMgPmdbAAqwrzOEHwAttZ8E+0sbABC1srCkcDCUDZQqmOA+oCkiytrU6Sp16vrARtdAAowANbCKSdQAHTsGDiVQACwA/JMNp0AFQ6DB6fRYcLmKylVwRFxOTEeLw+QrFJzBIylQHAsRgmKxOKkNBoP5kkGg+5pDJxPIbfGNYpiCCqCCkZQCADuDU5TDahO5jEktlIoKUahiNhqdTFUvFjFCUCMsuOP2pSR+qXSGSNOQ5hIJhKYpXKlXlitU6uKkuK0vIAHYXUEPUKIMKQB7tbq5Qb4kaWabMsILR7rTa/vbldVavUPW7GE0YcxCE0gA===

https://en.wikipedia.org/wiki/Loop-invariant_code_motion

https://github.com/dotnet/coreclr/blob/master/src/jit/loopcloning.cpp

JIT Derleyicisine Giriş – Tam sayı aritmetiği

.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

Ayrıca şurada da “A Brief History of Just-In-Time” şeklinde güzel bir makale var: http://eecs.ucf.edu/~dcm/Teaching/COT4810-Spring2011/Literature/JustInTimeCompilation.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.

Bunların hepsine genel olarak “managed execution” adı verilir.

Bu arada assembly kodları içerisinde sıklıkla görebileceğiniz bazı temel cpu register’ları şu şekilde;

EAX — Değerleri toplamak için kullanılan özel bir register

ECX — Counter (döngülerde ve string’lerde kullanılır)

EDX — EAX ile birlikte kullanılan, kısa süreli değişkenleri tutan register

EBX — Data depolamak için genel bir register. Dizilerde kullanılır.

Son olarak, JIT compiler açık kaynaktır. Şu Github sayfasından inceleme yapabilirsiniz:

https://github.com/dotnet/coreclr/tree/master/src/jit

Kaynaklar:

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators#right-shift-operator-

https://en.wikipedia.org/wiki/Logical_shift

https://en.wikipedia.org/wiki/Arithmetic_shift

https://en.wikipedia.org/wiki/X86_instruction_listings

https://en.wikipedia.org/wiki/Just-in-time_compilation

Minimal API | In-memory veritabanı ile CRUD işlemleri | 2. Kısım

Minimal API içinde, bir in-memory veritabanında update ve delete işlemlerini nasıl yapabileceğimizi öğrendik.

✅Github reposu: https://github.com/sonergonul/MinimalAPI

Update işlemi:

app.MapPut("/customers/{id}", async (CustomerDb db, Customer newCustomer, int id) =>
{
    var current = await db.Customers.FindAsync(id);

    if (current == null) return Results.NotFound();

    current.Name = newCustomer.Name;
    await db.SaveChangesAsync();

    return Results.NoContent();
});

Delete işlemi:

app.MapDelete("/customers/{id}", async (CustomerDb db, int id) =>
{
    var current = await db.Customers.FindAsync(id);

    if (current == null) return Results.NotFound();

    db.Customers.Remove(current);
    await db.SaveChangesAsync();

    return Results.Ok();
});

Kaynaklar:

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-6.0

https://docs.microsoft.com/en-us/aspnet/core/tutorials/min-web-api?view=aspnetcore-6.0&tabs=visual-studio

.NET 6 Yenilikleri – Nuget paket validasyonu

.NET 6 ile gelen yeniliklerden biri de EnablePackageValidation kullanımı.

Bu tag nuget paketlerinizin validasyonunun sağlıyor. Peki ne bu validasyonlar?

  • Sürümler arasında birbirini bozan değişiklik olup olmadığını doğrular.
  • Paketin, farklı runtime implementasyonlarına özgü uygulamalar için aynı public API yapısına sahip olup olmadığını doğrular.
  • Geliştiricilerin uygulanabilirlik açıklarını yakalamasına yardımcı olur.

Projenizin .csproj dosyası içerisinde PropertyGroup altında kullanabilirsiniz bu tag’ı.

<PropertyGroup>
  <EnablePackageValidation>true</EnablePackageValidation>
</PropertyGroup>

Kaynak:

https://docs.microsoft.com/en-us/dotnet/fundamentals/package-validation/overview

.NET 6 Yenilikleri – IANA Time zone desteği

.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 5 ve öncesinde yukarıdaki FindSystemTimeZoneById metodu TimeZoneNotFoundException fırlatırdı “Europe/Istanbul” için. Ama artık .NET 6 ile birlikte bu destek sağlanmış oldu.

TryConvertIanaIdToWindowsId ve TryConvertWindowsIdToIanaId statik metodları ile de windows time zone id’den iana time zone id’ye çevirme ve tam tersi işlemi yapabiliyoruz.

HasIanaId property’si ile de TimeZoneInfo instance’ının bir IANA id karşılığı olup olmadığını görebiliyoruz.

IANA time zone resmi sitesi: https://www.iana.org/time-zones

.NET 6 Yenilikleri – LINQ *OrDefault metodlarında default değerler

.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

Kaynak: Announcing .NET 6 — The Fastest .NET Yet

https://devblogs.microsoft.com/dotnet/announcing-net-6/#system-linq-firstordefault-lastordefault-singleordefault-overloads-taking-default-parameters

.NET 6 Yenilikleri – Chunk ile collection’ları eşit parçalara ayırma

.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()}");
}

Sonuç:

1 10
11 20
21 30
31 34

Kaynak: https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.chunk