Micro-optimization: Concatenating a string with a char using string.Concat
.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:
string Sample(string str, char c) => str + c;
is rewritten to:
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
:
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.
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
Method | Mean | Ratio | Allocated | Alloc Ratio |
---|---|---|---|---|
Operator_String_Char_String | 14.65 ns | 1.00 | 64 B | 1.00 |
Operator_String_String_String | 11.18 ns | 0.76 | 40 B | 0.62 |
String_Concat | 12.23 ns | 0.84 | 40 B | 0.62 |
#Additional resources
Do you have a question or a suggestion about this post? Contact me!