在C#中如何减少新字符串的产生

前言

最近看到了一段代码,在工作中,也是这样的用法,这样的用法其实现在是不太好的.先看看这一段代码.
public string HandlerActionName2(string actionName)
{
	if (actionName.EndsWith("Async", StringComparison.Ordinal))
	{
		actionName = actionName.Substring(0, actionName.Length - 5); //这里用Substring真的合适不? 
	}

	return actionName;
}

在调用HandlerActionName2的,如果还是会创建新新字符串,这里是不是就没必要产生的新字符串呢? 在.Net Core 2.1 提供了ReadOnlySpan用在这里是减少创建新字符串的.

public ReadOnlySpan<char> HandleActionName(ReadOnlySpan<char> actionName)
{
	if (actionName.EndsWith("Async", StringComparison.Ordinal))
	{
		//使用ReadOnlySpan的Slice进行切片,这个没有产生新的字符串
		actionName = actionName.Slice(0, actionName.Length - 5); 
	}

	return actionName;
}

性能基准测试

测试代码:
using System;
using BenchmarkDotNet.Attributes;

namespace CSharpBenchmarks.StringTest
{
	[MemoryDiagnoser]
	[DisassemblyDiagnoser(printSource: true)]
	public class StringToReadOnlySpan
	{
		/// <summary>
		/// 分别执行1024次和202048次
		/// </summary>
		[Params(1024, 2048)]
		public int Times { get; set; }

		/// <summary>
		///这里有两个字符串,用于测试
		/// </summary>
		[Params("TestAsync", "Test")]
		public string Names { get; set; }

		[Benchmark]
		public int ReadOnlySpanTest()
		{
			int count = 0;
			for (int i = 0; i < Times; i++)
			{
				count += HandleActionName(Names).Length;
			}
			return count;
		}

		[Benchmark(Baseline = true)]
		public int StringTest()
		{
			int count = 0;
			for (int i = 0; i < Times; i++)
			{
				count += HandlerActionName2(Names).Length;
			}
			return count;
		}

		public ReadOnlySpan<char> HandleActionName(ReadOnlySpan<char> actionName)
		{
			if (actionName.EndsWith("Async", StringComparison.Ordinal))
			{
				//使用ReadOnlySpan的Slice进行切片,这个没有产生新的字符串
				actionName = actionName.Slice(0, actionName.Length - 5);
			}

			return actionName;
		}

		public string HandlerActionName2(string actionName)
		{
			if (actionName.EndsWith("Async", StringComparison.Ordinal))
			{
				actionName = actionName.Substring(0, actionName.Length - 5); //这里用Substring真的合适不? 
			}

			return actionName;
		}
	}
}

笔记本执行结果:

笔记本执行的结果

台式机执行结果:

台式机执行的结果

根据测试结果:

1. 如果字符串中没有Async结尾的话,使用String比ReadOnlySpan比快35%和100%之间,具体取决于硬件了.

2. 如果字符中是以Async结尾的话,ReadOnlySpan<char>的Slice只是String.SubString的25%和29%,换而言之,SubString比Slice耗时多了3倍,因为Slice是没有产生新字符串,这一点从GC 0代回收次数也可以看到.减少内存分配,降低出发GC回收的次数.

秋风 2024-12-29