Optimizing the performance of your .NET applications requires efficient resource management. Memory allocations and deallocations must be performed optimally in performance-critical applications. One of the best strategies to perform resource management in an optimal way in an application is by allocating and deallocating string objects judiciously.
C# provides the string.Split() method to split strings in .NET applications. However, we have a better alternative, the ReadOnlySpan
To work with the code examples provided in this article, you should have Visual Studio 2022 installed in your system. If you don’t already have a copy, you can download Visual Studio 2022 here.
Create a console application project in Visual Studio 2022
First off, let’s create a .NET Core 9 console application project in Visual Studio 2022. Assuming you have Visual Studio 2022 installed, follow the steps outlined below to create a new .NET Core 9 console application project.
- Launch the Visual Studio IDE.
- Click on “Create new project.”
- In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed.
- Click Next.
- In the “Configure your new project” window, specify the name and location for the new project.
- Click Next.
- In the “Additional information” window shown next, choose “.NET 9.0 (Standard Term Support)” as the framework version you would like to use.
- Click Create.
We’ll use this .NET 9 console application project to work with the the ReadOnlySpan
Why do we need to split strings?
When working in applications, you will often need to manipulate text data by splitting strings, joining strings, creating substrings, etc. Typically, you will encounter the need to split strings in the following scenarios:
- Processing large text files
- Parsing arguments passed to an application from the command line
- Tokenizing a string
- Parsing CSV files
- Processing log files
The String.Split() method in C#
The String.Split() method creates an array of substrings from a given input string based on one or more delimiters. These delimeters, which act as separators, may be a single character, an array of characters, or an array of strings. The following code snippet shows how you can split a string using the String.Split() method in C#.
string countries = "United States, India, England, France, Germany, Italy";
char[] delimiters = new char[] { ',' };
string[] countryList = countries.Split(delimiters,
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
foreach (string country in countryList)
Console.WriteLine(country);
When you execute the preceding piece of code, the list of countries will be displayed at the console window as shown in Figure 1.
IDG
The ReadOnlySpan.Split() method in C#
While the String.Split() method might be convenient and easy for you to use, it has performance drawbacks. It is not a good choice in performance-critical applications because of its resource allocation overhead. Whenever you use the Split() method to split a string, a new string becomes allocated for each segment.
Additionally, the Split() method stores all the segments parsed into an array of strings. This uses a significant amount of memory, particularly when you’re dealing with large strings. With .NET 9, you can split strings in a much more efficient way using the ReadOnlySpan
The code snippet below shows how we can split the same string in the above example by using the ReadOnlySpan
ReadOnlySpan readOnlySpan = "United States, India, England, France, Germany, Italy";
char[] delimiters = new[] { ',' };
foreach (Range segment in readOnlySpan.Split(delimiters))
{
Console.WriteLine(readOnlySpan[segment].ToString().Trim());
}
Note that by using ReadOnlySpan
Benchmarking the String.Split() and ReadOnlySpan.Split() methods
Let us now benchmark the performance of both approaches to splitting strings in C#. To do this, we’ll take advantage of an open source library called BenchmarkDotNet. You can learn how to use this library to benchmark applications in .NET Core in a previous article.
To benchmark performance of the two approaches, first create a new C# class named SplitStringsPerformanceBenchmarkDemo and write the following code in there.
[MemoryDiagnoser]
public class SplitStringsPerformanceBenchmarkDemo
{
readonly string str = "BenchmarkDotNet is a lightweight, open source, powerful.NET library " +
"that can transform your methods into benchmarks, track those methods, " +
"and then provide insights into the performance data captured. " +
"It is easy to write BenchmarkDotNet benchmarks and the results of the" +
" benchmarking process are user friendly as well.";
[Benchmark]
public void SplitStringsWithoutUsingReadOnlySpan()
{
char[] delimiters = new char[] { ',' };
string[] list = str.Split(delimiters);
foreach (string country in list)
{
_ = country;
}
}
[Benchmark]
public void SplitStringsUsingReadOnlySpan()
{
ReadOnlySpan readOnlySpan = str;
char[] delimiters = new char[] { ',' };
foreach (Range segment in readOnlySpan.Split(delimiters))
{
_ = readOnlySpan[segment].ToString().Trim();
}
}
}
You should decorate each method to be benchmarked using the [Benchmark] attribute. In this example, we have two methods that will be benchmarked. One of them uses the ReadOnlySpan
Execute the benchmarks
The following code snippet shows how you can run the benchmarks in the Program.cs file.
var summary = BenchmarkRunner.Run(typeof(SplitStringsPerformanceBenchmarkDemo));
Remember to include the following namespaces in your program.
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
Finally, to compile the application and execute the benchmarks, run the following command in the console.
dotnet run -p SplitStringsPerformanceBenchmarkDemo.csproj -c Release
Figure 2 shows the results of the executed benchmarks.
IDG
As you can see from the benchmarking results in Figure 2, the ReadOnlySpan
The ReadOnlySpan
While Span