Palindrome Number

I few years ago I explored the palindrome problem. At the time I focused on inputs of strings. That previous post has described a few solutions from a simple implementation to vectorized solutions.

In this post I will focus on the same problem but with numbers as the input. Is a number palindrome? sounds like a simple question but requires some clarifications to before answering. In this post I use the following constraints:

  • Only positive integers and 0 zero are considered valid inputs (uints).

  • A number is palindrome if the digits of it in base 10 represent a palindrome. All other non-digit characters (., ,, -, etc.) are omitted.

Improving the Vectorized Palindrome

Find out more


Ref-Readonly and In

The current version of C# (C# 13 at the time of writing) has in and ref keywords. In this post I investigate using these keywords as parameter modifiers. Specifically, the difference between ref readonly and the in parameter modifier.

Both parameter modifiers are well-suited for methods that have large struct parameters. Regular struct parameters follow copy-by-value semantics. Larger struct copies incur a performance penalty as the larger the struct is the more data has to be copied. I investigated the cost of copies in a post about inline arrays. One approach to address this problem is by passing a reference to the struct parameters.

In modifier

An author of a method could use the in parameter modifier for value type parameters. In the following example as RegularStruct is passed to a method named PassByIn, with the in modifier:

Find out more


String.Create with Spans

One new C# feature with .NET 9 is the ability to define allows ref struct as a generic type constraint. It sounds contradictory to refer to this as a constraint, because it allows more types to be used with the given method. However, it can also limit how those generic type parameters maybe used in methods. Allowing ref structs allows ref struct types to be used as generic type parameters. The most common ref struct types are the built-in Span<T> and ReadOnlySpan<T> but this constraint allows for any custom ref structs as well. In C# ref struct types are special as they must live on the stack, and must not escape to the managed heap.

The C# compiler makes sure that ref struct types are used correctly, and in this sense, it poses as a constraint. For example, in the code snippet below MyType compiles without the constraint:

public class MyType<T> // where T : allows ref struct
{
    private T _field;
    public T MyCreate(Func<T> myfunc)
    {
        var value = myfunc();
        _field = value;
        return value;
    }
}

However, if we uncomment the where T : allows ref struct constraint, the compiler issues the following error for _field: Field or auto-implemented property cannot be of type 'T' unless it is an instance member of a ref struct.

Find out more


Testing Locks is Difficult

I have recently worked with multithreaded code using shared state, that required mutual exclusion. In .NET there are numerous different types to accomplish mutual exclusion, the C# language even provides a lock keyword. Depending on the use-case one can use Monitor, lock on reference types, SemaphoneSlim, ReaderWriterLockSlim, Interlocked, SpinLock concurrent collections, or the .NET 9 introduced Lock type. This latter one also works with the lock language keyword, and it brings much of the underlying native implementation to the managed space. This results in some minor performance improvements compared to locking on any other reference type.

Achieving the above improvement seems straightforward: change the object's type that is used within a lock keyword. However, lock might be still slower to other types of locks as the 'best' locking solution depends on many factors:

  • number of threads accessing the lock

  • the frequence of the threads accessing the lock

  • duration inside the lock

  • contention

  • CPU, architecture, OS, etc.

  • memory model

  • etc.

In general, certain locks might fit better for certain uses cases (for example, when using collection, async code, granular locks etc.), which drives the decisions on the lock that suits the best for an application. To prove that one locking solution is more performant than another is only possible by performance measuring an actual application on a production-like load. This also means that the same code might perform differently from application to application.

Find out more


Struct Equality and SkipLocalsInit

I have recently learned an interesting behavior of the .NET runtime on equality from a post on Mastodon. The post points out that RuntimeHelpers.Equals returns whether the input arguments' underlying memory are equal, which may not be true always, even when the values are equal.

Under the hood RuntimeHelpers.Equals uses memcmp to compare the memory representation of two objects.

Padding

In C# structs may have paddings in the memory representation. For example, the following struct will have a padding on x64, so that the value is aligned to 8 bytes:

Find out more