Laszlo

Hello, I am Laszlo

Software-Engineer, .NET developer

Contact Me

Know Your Data

There are many social posts, blog posts, and code suggestions online for .NET code snippets titled "Use X instead of Y" or "Why X is better than Y". These posts usually (but not always) include a basic performance comparison using BenchmarkDotNet to validate their catchy titles.

At first glance, these suggestions appear justified by the measurements. However, upon closer inspection, many important details are typically excluded:

  • What version of .NET (SDK and Runtime) was used?
  • What version of OS was used?
  • What is the underlying hardware architecture (ARM or x64)?
  • Does the hardware support vectorized operations?
  • What are the sizes of the vector registers?
  • What is the memory read latency?
  • What are the CPU cache sizes?
  • What branch prediction algorithm does the CPU use?
  • What are the input data types (structs, classes, primitives)?
  • What is the input data structure?
  • What is the size of the input data (bytes/array length)?
  • What is the data access pattern?
  • And many more factors

Since explaining all these factors is beyond this post's scope, I will focus on data access patterns and data sizes. For those interested in a deeper analysis, I recommend reading Pro .NET Benchmarking by Andrey Akinshin, which explains many common pitfalls.

Find out more »


Request Cancellation in ASP.NET Core with HTTP/2

ASP.NET Core allows to create endpoints that receive a cancellation token, such as below:

app.MapGet("/path", async (CancellationToken token) =>
{
});

In this post, I will explore how a server (such as Kestrel) may fire the token to be canceled.

Cooperative Cancellation

Find out more »


Distinct and HashSet

A common pattern in business-as-usual (BAU) applications is that a class in the business logic layer requests a set of entities by their integer IDs from a repository class. Here is one possible implementation of this pattern:

  1. A method in the business layer receives a list of integers as an argument (List<int>).
  2. It applies filtering and selection on these integers by creating a new enumerable.
  3. It extracts the unique values from the filtered and projected numbers.
  4. It passes the filtered, selected unique numbers to a query method in the repository class.
  5. This repository method processes the passed in integer enumerable:
    • If there aren't any numbers in the enumeration, it returns null (or empty results).
    • Otherwise, it copies the numbers into an SQL query and returns the query's result.

In this blog post, I will focus on two possible implementations to produce the unique set of integers:

  1. Using the Distinct LINQ extension method.
  2. Using the ToHashSet<T> method to materialize the result before passing it to the repository class.

Find out more »


Extension Members in C# 14

In this post, I explore C# 14's extension members feature and how it is compiled into IL code.

From Extension Methods to Extension Members

Previous versions of C# allowed developers to declare extension methods. The code snippets in this post are for demonstration purposes only and should not be used directly in production applications:

string value = "ThisIsPascalCased";
Console.WriteLine(value.ToCamel());

public static class MyExtensions
{
    public static string ToCamel(this string str) => str.Length switch
    {
        0 => string.Empty,
        1 => str.ToLowerInvariant(),
        _ => new([char.ToLower(str[0]), .. str[1..]])
    };
}

Find out more »


Using ClrMD for string analysis

Analysis of a .NET application's memory dump often reveals that byte[] and string types are among the most commonly allocated objects. This is not surprising, as most UI and web applications use string types to display data to users or byte[]/string to pass data over the network in HTTP/2 requests and responses. These string objects commonly represent configuration values, string literals, or data allocated by dependent libraries.

Analyzing strings in such a noisy environment is challenging. While common tools like Visual Studio, PerfView, and dotnet-dump provide high-level analysis, drilling into the details is often difficult. ClrMD is one of the tools that enables programmatic analysis of memory dumps. In my previous post Getting Started with ClrMD, I introduced how to analyze a full memory dump of a .NET application. This post explores a specialized analysis for strings: identifying application-defined types that hold references to large strings. These are the strings that developers have direct control over in their application code.

Execution Steps

First, collect a memory dump of a running application:

Find out more »