Micro-optimization: Concatenating a string with a char using string.Concat

 
 
  • Gérald Barré

.NET introduced some new methods that allow slight performance improvements in string concatenations. In this post, I'll show you how to concatenate a string and a char using string.Concat and a ReadOnlySpan<char>.

When you concatenate string using the + operator, the compiler rewrites the expression to string.Concat. When a type is not a string, the compiler can add a ToString call. This is the case when you concatenate a char with a string. For instance, the following code:

C#
string Sample(string str, char c) => str + c;

is rewritten to:

C#
string Sample(string str, char c) => string.Concat(str, c.ToString());

Because of the ToString call, you get one allocation. This means that garbage collection will be triggered. If you concatenate a char with a string many times, this can have a significant impact on the performance.

Recently, string.Concat was extended to allow to concatenate ReadOnlySpan<char>s. So, if you can convert the char to a ReadOnlySpan<char>, you can avoid the ToSring call. Using .NET 7, this is easily possible using new ReadOnlySpan<char>(in c).

This means you can write the following code to remove the allocation for the char:

C#
string Sample(string str, char c) => string.Concat(str, new ReadOnlySpan<char>(in c));

Let's write a benchmark using BenchmarkDotnet. If you are not familiar with this tool, you can read my previous post about comparing implementations with BenchmarkDotnet.

C#
using BenchmarkDotNet.Attributes;

namespace Benchmarks;
[MemoryDiagnoser]
public class StringConcatBenchmark
{
    private string Part1 = "abc";
    private char Part2_Char = 'd';
    private string Part2_String = "d";
    private string Part3 = "efg";

    [Benchmark(Baseline = true)]
    public string Operator_String_Char_String() => Part1 + Part2_Char + Part3;

    [Benchmark]
    public string Operator_String_String_String() => Part1 + Part2_String + Part3;

    [Benchmark]
    public string String_Concat() => string.Concat(Part1, new ReadOnlySpan<char>(in Part2_Char), Part3);
}
BenchmarkDotNet=v0.13.4, OS=Windows 11 (10.0.22623.1245)
AMD Ryzen 7 5800X, 1 CPU, 16 logical and 8 physical cores
.NET SDK=7.0.200-preview.22628.1
  [Host]     : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
  DefaultJob : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
MethodMeanRatioAllocatedAlloc Ratio
Operator_String_Char_String14.65 ns1.0064 B1.00
Operator_String_String_String11.18 ns0.7640 B0.62
String_Concat12.23 ns0.8440 B0.62

#Additional resources

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub