在.Net 7中使用Span下IndexOfAny和IndexOfAnyExcept
前言
这篇文章其实比 在.Net 7 String的StartsWtih和EndsWith性能改进 时间上还要早一些,只是一直处于没完成的状态.进入正题.主要是看到这个Issue:- Add initial version of {Last}IndexOfAnyExcept (#67941)
在项目中经常使用IndexOf/Contains判断字符串是否包含某个字符/字符串,一直没有是用IndexOfAny(.Net Core 2.0新增).而IndexOfAnyExcep是在.Net 7中新增的,这两个API都是字符串开始查找的,与这两个API相反的,分别是LastIndexOfAny(.Net Core 2.1新增)和LastIndexOfAnyExcept(.Net 7新增),这两个都是从字符串的末尾开始查找的.
这里只是测试前两个IndexOfAny和IndexOfAnyExcept.
测试代码
using System;
using BenchmarkDotNet.Attributes;
namespace CSharpBenchmarks.SpanTest
{
[MemoryDiagnoser]
[DisassemblyDiagnoser(printSource: true)]
public class IndexOfAnyExceptTest
{
[Params(1024, 2048)]
public int Times { get; set; }
[Params("hello world\t", "\thello csharp")]
public string Chars { get; set; }
[Benchmark]
public int ForIndexOfTest()
{
int sum = 0;
for (int i = 0; i < Times; i++)
{
for (int j = 0; j < Chars?.Length; j++)
{
char c = Chars[j];
if (c == '\t' || c == '\n' || c == '\r')
{
sum += 1; //这里只是不让JIT将代码优化掉
break;
}
}
}
return sum;
}
[Benchmark]
public int SpanIndexOfAnyTest()
{
int sum = 0;
for (int i = 0; i < Times; i++)
{
if (Chars.AsSpan().IndexOfAny("\t\r\n") > -1)
{
sum += 1; //这里只是不让JIT将代码优化掉
}
}
return sum;
}
[Benchmark]
public int ForNoContainsTest()
{
int sum = 0;
for (int i = 0; i < Times; i++)
{
for (int j = 0; j < Chars?.Length; j++)
{
char c = Chars[j];
if (c != '\t' || c != '\n' || c != '\r')
{
sum += 1; //这里只是不让JIT将代码优化掉
break;
}
}
}
return sum;
}
[Benchmark]
public int SpanIndexOfAnyExceptTest()
{
int sum = 0;
for (int i = 0; i < Times; i++)
{
if (Chars.AsSpan().IndexOfAnyExcept("\t\r\n") > -1)
{
sum += 1; //这里只是不让JIT将代码优化掉
}
}
return sum;
}
}
}
根据上图的测试结果: IndexOfAny和IndexOfAnyExcept在最好的情况下(\thello csharp),是比我们直接循环要慢上不少(真实情况下,会从末位开始遍历),但在最坏的情况(hello world\t),IndexOfAny竟然比我们直接循环快上不少.IndexOfAny和IndexOfAnyExcept在不需要性能的地方还是可以使用的.
秋风
2022-07-04