All posts by Soner Gönül

Switch içerisinde Hash Table optimizasyonu [C#]

C# compiler’ının switch statement’ı içerisinde 7. case’den sonra bir hash table optimizasyonu yaptığını biliyor muydunuz?

C# compiler’ı switch statement‘ını 6 tane case sayısına ulaşana kadar lowering mekanizması ile standart if else ifadelerine çeviriyor.

Fakat case sayısı 7’ye ulaştığında lowering mekanizması akıllıca bir optimizasyona gidiyor. Bu, C# compiler’ın işini zamanında yapmasını sağlayan çok akıllı bir optimizasyon. Bu optimizasyon, aynı string’lerin hash code’larının aynı olması gerektiği gözlemine dayanıyor. Bu yüzden string’leri teker teker eşitliğini kontrol etmek yerine, compiler hedef string’in bir hash’ini hesaplıyor table-lookup mantığında çalışmasını sağlıyor yani O(1) zamanda.

Bu Roslyn’in github sayfasında şöyle bahsedilmiş:

The string switch codegen in Roslyn is completely new. Roslyn does not use dictionaries to avoid allocations and a potentially huge penalty when a string switch is execute for the first time. Roslyn uses a private function that maps strings to hash codes and a numeric switch. In some sense this is a partial inlining of the former technic that used static dictionaries.

Kaynaklar:

https://sharplab.io/#v2:EYLgtghglgdgPgAQEwEYCwAoBBmABM3AYVwG9NcL89YAXXAWQAoEUAGXAZwEpdzKyMlIZwDuUGgGMAFo259hpeQsoSIHAKa4ARBC0h8AdlwoA3EuW5VG7cD2HcSM4Isq1mrRLsIj2Jy9fWWgAmXkYALH7+lm7a6qG4AKyR/lbuAGbxAGzJLqnaAObxBjkWQeppEACuADY0+t64ALSm5pQAvkodGG1AA=

https://en.wikipedia.org/wiki/Switch_statement#Advantages_and_disadvantages

https://github.com/dotnet/roslyn/blob/main/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs

C# 11 ile Gelen Yenilikler

Microsoft’un yayınladığı blog yazısına istinaden C# 11 ile gelebilecek özelliklere göz attık.

Öncelikle .NET 7 henüz long term support olmadığı için, C# 11’i kullanmak adına bazı ayarlar yapmanız gerekiyor. Bunun için visual studio 2022 17.1 versiyonunu indirmeli (community, professional veya enterprise edition fark etmez) ve .NET SDK’sının 6.0.200 versiyonunu kurmanız gerekiyor. Son olarak ta, .NET 6 ile oluşturduğunuz projenizin csproj dosyasında LangVersion tag’ını preview olarak eklemeniz gerekiyor.

✅ String interpolation’daki yenı satır kısıtı

Bildiğiniz gibi verbatim string literal‘ler içerisinde yeni satırda kullanım sağlayabiliyorduk fakat non-verbatim string literaller içerisinde bunun için \r\n kullanmamız gerekiyordu. C# 11 ile birlikte, non-verbatim interpolated string’ler için de yeni satır kısıtını kaldırmışlar.

var v = $"sayı\t: { 5.tostring()
.ToString(
Cultureınfo.InvariantCulture)}";

✅ List patterns (opsiyonel slice patterns)

Liste ve dizilerde pattern matching desteği geliyor. List pattern’lar köşeli parantezlerle belirtilirken, slice pattern’lar ise iki nokta (..) ile belirtiliyor ve isterseniz var pattern’ı da kullanabiliyorsunuz. İlerleyen zamanlarda IEnumerable tipler için de list pattern desteği getirmeyi planlıyorlarmış.

✅ Parameter null-checking (!! operatörü)

Ve geldik .NET dramasına dönen o yeniliğe. Hem Twitter hem de Github’ta çok fazla tartışıldı bu operatör. Ek olarak  @Sedat Kapanoglu  da bu yeniliği neden beğenmediğini yine aynı thread içerisinde paylaştı.

https://github.com/dotnet/csharplang/discussions/5735#discussioncomment-2153360

Özetle, C#’ta bir argümanın null kontrolü klasik olarak aşağıdaki şekildeydi;

void M1(string s)
{
   if (s is null)
   {
      throw new ArgumentNullException(nameof(s));
   }
}

.NET 6 ile ThrowiIfNull statik metodu geldi.

void M2(string s)
{
   ArgumentNullException.ThrowIfNull(nameof(s));
}

ve C# 11 ile birlikte !! (bang bang) operatörü hayatımıza girecek aynı işlevi gören.

void M3(string s!!)
{
}

Bu arada bu özellik nullable reference types ile birlikte çalışabiliyor. NRT compile time’da, parameter null-checking runtime’da kontrol şeklinde düşünebilirsiniz.

Lambda parametreleri desteği var. Indexer parametreleri için kullanılabilir. Get ve set içerisinde kontrol edilir. Constructor’lar için “field initialization”‘dan önce kontrol ediliyor. async metodlarda da kullanım mevcut.

Kaynak:

https://devblogs.microsoft.com/dotnet/early-peek-at-csharp-11-features/

https://github.com/dotnet/csharplang/discussions/5735

https://sharplab.io/#v2:EYLgtghglgdgPgAQEwEYCwAoBBmABM3AYVwG9NcL8AWXAWRQAoEUAGXAZwEpzKyNKBuKADNcDdkIkwArgBtZ3foIp9lygC4ALAE4B7AO64YAU0MBBbQHNpYYzHUA5ObICiADwDGxgA7qoumAYYCFtdYXFOTgBuHmUAX1jcBIxEgHpU3AA6BxcAFVwANkxEhBpaJCZWDkUBVTULa1t7J3l3L19/GEzcnQMASWEW2SCQ4zCI6MTkkrLsSrZ2AEJFmt4pzDigA=

Visual Studio 2022 17.1 ile gelen yenilikler

Visual Studio 17.1 sürümü 15 Şubat 2022 tarihinde release edildi. Bu videoda bu sürüm ile gelen yeniliklere göz attık.

— Index’lenmiş dosyalar ile daha hızlı arama özelliği geldi. Bunu arka planda task manager ile görebileceğiniz ServiceHub.IndexingService.exe uygulaması ile sağlamaya başladılar. Bu özelliği Tools -- Options -- Environment -- Preview Features -- Enable indexing for faster find experience adımlarından açıp kapatabilirsiniz.

— Kod dosyalarınızı otomatik olarak senkronize etme. Bu özellik Tools -- Options -- Environment -- Documents -- Automatically save files when Visual Studio is in the background adımlarından açıp kapatabilirsiniz. Community edition’da yok sanırım, sadece professional ve enterprise müşterilerde var galiba bu.

— Branch’ler arası karşılaştırma.

C# 11 ile gelmesi planlanan Raw string literal özelliğine otomatik olarak regular string literal veya verbatim string literal’lerden Quick Tips bubble’ı ile çevirme.

Yani;

var s = "\"foo\r\nbar\"";

string’ini;

var s2 = """
    "foo
    bar"
    """;

yapabiliyor. Raw string literal’ler string interpolation’ı destekliyor ve hizalamayı önemsemiyorlar gördüğüm kadarıyla.

— Line staging

Geliştirmesi hala devam ediyormuş. Sanırım bir dosya içerisinde belirli bir satırı stage’e atma işlemi.

— C# 7 – Tuple swap

var temp = args[1];
args[1] = args[0];
args[0] = temp;

kodunu “Quick Actions” ile

(args[0], args[1]) = (args[1], args[0]);

koduna çevirebiliyorsunuz.

— Display diagnostics inline

Bu özelliğe Tools -- Options -- Text Editor -- C# -- Advanced ile gidebiliyorsunuz. Popup ile görebileceğiniz warning’leri ekranda direkt olarak çıkartabiliyor.

Örneğin sadece;

int second = 1;

yazdığınızda, değişkenin üstüne gittiğinizde çıkan popup’ı ekrana direkt olarak basabiliyor. Deneme aşamasında şu anda.

— Simplify property pattern

C# 10 ile gelen extended property patterns özelliğine “Quick Tips” bubble’ı ile otomatik olarak çevirim sağlayabiliyorsunuz.

Yani;

if (t is { Assembly: { EntryPoint: { CallingConvention: CallingConventions.Any } } }) ;

kodunu

if (t is { Assembly.EntryPoint.CallingConvention: CallingConventions.Any }) ;

koduna çevirebiliyoruz.

Kaynaklar:

https://devblogs.microsoft.com/visualstudio/visual-studio-2022-17-1-is-now-available/

https://docs.microsoft.com/en-us/visualstudio/releases/2022/release-notes

Microsoft Mülakat Sorusu | Insertion Sort List

Leetcode içerisinde bulunan “Insertion Sort List” sorusunun açıklaması ve çözümü. Bu soruda size verilen linked list içerisindeki node’ları insertion sort kullanarak sıralamanız isteniyor.

➡️ Leetcode 147. Insertion Sort List: https://leetcode.com/problems/insertion-sort-list/

➡️ Problem açıklaması:

Given the head of a singly linked list, sort the list using insertion sort, and return the sorted list’s head. The steps of the insertion sort algorithm:

– Insertion sort iterates, consuming one input element each repetition and growing a sorted output list.

– At each iteration, insertion sort removes one element from the input data, finds the location it belongs within the sorted list and inserts it there.

– It repeats until no input elements remain.

The following is a graphical example of the insertion sort algorithm. The partially sorted list (black) initially contains only the first element in the list. One element (red) is removed from the input data and inserted in-place into the sorted list with each iteration.

Example 1:

Input: head = [4,2,1,3]

Output: [1,2,3,4]

Example 2:

Input: head = [-1,5,3,4,0]

Output: [-1,0,3,4,5]

Constraints:

The number of nodes in the list is in the range [1, 5000].

-5000 <= Node.val <= 5000

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

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

Minimal API ile in-memory veritabanı ile CRUD işlemlerinden listeleme ve ekleme işlemlerine baktık.

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

Bunun için Nuget içerisindeki Microsoft.EntityFrameworkCore.InMemory paketini kurduk öncelikle. Bu size in-memory olarak bir veritabanı sağlıyor. Daha sonra entity framework içerinde nasıl DbContext‘ten bir sınıf türetip içerisinde DbSet‘ler kullanarak tablolar oluşturuyorsak burada da aynı yapıyı kullanıyoruz. Sonrası aşağıdaki gibi;

Listeleme:

app.MapGet("/customers", async (CustomerDb db) => await db.Customers.ToListAsync());

Ekleme:

app.MapPost("/customers", async (CustomerDb db, Customer cus) => 
{
    await db.Customers.AddAsync(cus);
    await db.SaveChangesAsync();
    return Results.Created($"/customers/{cus.Id}", cus);
});

ID bazlı listeleme:

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

Kaynaklar:

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

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

Minimal API | Kuruluş ve Swagger Kurulumu

Minimal API‘ler, .NET 6 ile birlikte gelen, minimum bağımlılıkla HTTP API’leri oluşturmak için tasarlandı. ASP.NET Core‘da yalnızca minimum dosyaları, özellikleri ve bağımlılıkları dahil etmek isteyen mikro hizmetler ve uygulamalar için idealdirler.

Minimal API için en basitinden 4 satır koda ihtiyacımız var.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();

İşte bu kadar. Elinizde çalışan bir http restful bir api’ınız oldu. Bu geliştirdiğiniz api artık postman üzerinden denemeye hazır.

Swagger nedir peki?

Swagger, JSON kullanılarak ifade edilen RESTful API‘leri açıklamak için bir arayüz tanımlama dilidir. Swagger, RESTful web hizmetlerini tasarlamak, oluşturmak, dökumante etmek ve kullanmak için bir dizi açık kaynaklı yazılım aracıyla birlikte kullanılır. Swagger, otomatik dokümantasyon, kod oluşturma (birçok programlama dilinde) ve test senaryosu oluşturma içerir.

Swagger için kullanılan methodların açıklaması şu şekilde;

app.UseSwagger — ara katman yazılımının oluşturulan OpenAPI açıklamasını JSON içeriği olarak sunmasını sağlar.

app.UseSwaggerUI — Middleware’in Swagger UI öğelerine hizmet etmesini sağlar.

SwaggerEndpoint — OpenAPI açıklamasının endpoint’ini belirtir.

Peki klasik controller ile geliştirilen api’lerden ne farkı var Minimal api’nin?

☑️Filtre desteği yok. AuthorizationFilter, ActionFilter, ExceptionFilter, ResultFilter gibi mesela.

☑️ModelValidator desteği yok.

☑️ModelBinder desteği yok. Custom olarak eklenebilir tabi bu.

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

Kaynaklar:

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

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

https://medium.com/geekculture/minimal-apis-in-net-6-a-complete-guide-beginners-advanced-fd64f4da07f5

https://dotnetcoretutorials.com/2021/07/16/building-minimal-apis-in-net-6/

https://dev.to/moe23/getting-started-with-minimal-apis-in-net-6-4mi4

BenchmarkDotNet | Baseline ve Jobs 3. Kısım

BenchmarkDotNet içerisinde baseline ve Jobs konularına baktık bu videoda. Bu video benchmarkdotnet içerisinde şu konulara göz attık;

💎 Baseline

[Benchmark(baseline = true)]

public void Sleep200() ==> Thread.Sleep(200);

💎 SimpleJobs

[SimpleJob]

💎 RunStrategy

[SimpleJob(runStrategy: RunStrategy.Throughput)] 

💎 RuntimeMoniker

[SimpleJob(runtimeMoniker: RuntimeMoniker.Net50)] 

💎 Orderer

[Orderer(SummaryOrderPolicy.SlowestToFastest)]

Özellikle RuntimeMoniker kullanırken seçtiğiniz ortamlara ait SDK’ları bilgisayarınıza kurmayı ve projenizin csproj dosyasına hedef ortam bilgisini TargetFrameworks tag’leri arasına yazmayı unutmayın.

Kaynak: https://benchmarkdotnet.org/articles/overview.html

BenchmarkDotNet | Parametrelendirme 2. Kısım

Benchmarkdotnet içerisinde parametrelendirme özelliklerinden bahsettim bu videoda.

✅ Best Practices

➡️Release build (debug mode 10-100 kat daha yavaş)

➡️Farklı ortamları deneyin (CLR, .NET Framework ve JIT)

➡️Math.Exp(1); şeklinde ölü kodları engelleyin (JIT bu kodları optimize ediyor). Mümkünse return Math.Exp(1); şeklinde kullanın.

➡️Standart OS process’leri ve benchmark process’i hariç diğer tüm process’leri öldürün.

➡️Bilgisayarını şarja takın ve maksimum performans modunda çalıştırın.

✅ İterasyonlar

➡️Pilot: Seçilecek en iyi işlem sayısı.

➡️OverheadWarmup, OverheadWorkload: Değerlendirilecek ek yük.

➡️ActualWarmup: Isınma süreci.

➡️ActualWorkload: Gerçek ölçümler.

➡️Result = ActualWorkload – MedianOverhead

Kaynak: https://benchmarkdotnet.org/articles/features/parameterization.html

BenchmarkDotNet | Kurulumu ve Temelleri 1. Kısım

Yazdığınız bir kod parçası ne kadar verimli? Az önce yaptığınız değişiklik uygulamanızı iyileştirecek mi? Bunu nereden biliyorsunuz? Bu değişiklik ne kadar önemliydi? Bunlar kıyaslama yani benchmarking yapmadan cevaplanamayacak sorular. Bu video serisinde, kodun ne kadar verimli olduğunu belirlemek için Benchmarkdotnet‘in kodu değerlendirmek için nasıl kullanılacağına ve ardından kodun değiştirilmiş bir versiyonuna kıyasla ne kadar verimli olduğuna bakacağız.

BenchmarkDotNet, yöntemleri karşılaştırma ölçütlerine dönüştürmenize, performanslarını izlemenize ve tekrarlanabilir ölçüm deneylerini paylaşmanıza yardımcı olur. Birim testleri yazmaktan daha zor değil! BenchmarkDotNet sizi popüler kıyaslama hatalarından korur ve kıyaslama tasarımınızda veya elde ettiğiniz ölçümlerde bir sorun olduğunda sizi uyarır. Sonuçlar, denemenizle ilgili tüm önemli gerçekleri vurgulayan kullanıcı dostu bir biçimde sunulur. Proje, .NET Runtime dahil olmak üzere 6800’den fazla proje tarafından benimsenmiş ve .NET Foundation tarafından desteklenmektedir.

Tipik bir durumda ne yapmanız gerektiğini düşünelim. İlk olarak, bir pilot deney yapmalı ve en iyi yöntem çağırma sayısını belirlemelisiniz. Ardından, birkaç ısınma yinelemesi gerçekleştirmeli ve karşılaştırma ölçütünüzün sabit bir duruma ulaştığından emin olmalısınız.

Elle yazılmış birçok kıyaslama, yanlış iş kararlarına yol açan yanlış sayılar üretir. BenchmarkDotNet, sizi kıyaslama tuzaklarının çoğundan korur ve yüksek ölçüm hassasiyeti elde etmenizi sağlıyor.

Mükemmel yöntem çağırma sayısı, ısınma ve gerçek yineleme sayısı hakkında endişelenmemelisiniz: BenchmarkDotNet en iyi kıyaslama parametrelerini seçmeye çalışır ve ölçüm öngörüsü ile tüm kıyaslama çalıştırmalarının toplam süresi arasında iyi bir denge elde etmeye çalışır.

Videoda neler öğrendik?

➡️ Benchmark attribute kullanımı

➡️ MemoryDiagnoser attribute kullanımı

➡️ BenchmarkRunner.Run kullanımı

✅ Gen 0, Gen 1 ve Gen 2 nedir ve nasıl çalışır?

Küçük .NET nesneleri, küçük heap’lere tahsis edilir. Bunlardan üç tane vardır: Gen 0, Gen 1 ve Gen 2. Gen 0’a yeni nesneler yerleştirilir.

Gen 0 dolduğunda, .NET Garbage Collector çalışır, artık ihtiyaç duyulmayan nesneleri elden çıkarır ve diğer her şeyi Gen 1’e taşır. Gen 1 dolarsa, GC yeniden çalışır, aynı zamanda Gen 1’deki nesneleri Gen 2’ye taşır.

Gen 2 dolduğunda tam bir GC çalışması gerçekleşir. Bu, gereksiz Gen 2 nesnelerini temizler, Gen 1 nesnelerini Gen 2’ye taşır, ardından Gen 0 nesnelerini Gen 1’e taşır ve son olarak referans verilmeyen her şeyi temizler.

Kaynaklar:

✔️ https://benchmarkdotnet.org/

✔️ https://www.red-gate.com/products/dotnet-development/ants-memory-profiler/learning-memory-management/memory-management-fundamentals