为什么 string.Contains 比 string.IndexOf 表现更好?

问题描述 投票:0回答:1

我正在尝试优化一种方法。

原始方法如下:

private const string Separator = "::";
private char[] SeparatorChars = Separator.ToCharArray();

public string GetSubName(string name)
{
    if (name.Contains(Separator))
    {
        return name.Split(SeparatorChars)[0];
    }
    
    return "INVALID";
}

如果

name
不包含
::
,则返回
INVALID
,否则返回
Split
::
生成的数组中的第一个元素。

我写了以下优化方法:

public string GetSubNameOpt(string name)
{
    var index = name.IndexOf(Separator, StringComparison.Ordinal);
    if (index >= 0)
    {
        return name.AsSpan().Slice(0, index).ToString();
    }

    return "INVALID";
}

目标是省略字符串上的第二次 O(N) 迭代,以便在字符串包含

::
子字符串时将其拆分。

我使用 BenchmarkDotNet 对这两种方法进行了基准测试,结果如下

现在..正如预期的那样,由于使用了 AsSpan 并删除了字符串的 1 O(N) 迭代,优化方法在“名称包含 :: case”中的时间和内存方面更好,但令我惊讶的是非优化方法对于无效情况更好。

编辑 使用非空、不包含

::
的情况运行。

同样,优化方法速度较慢...

您能解释一下是什么原因导致了这种行为吗?

c# string performance benchmarking .net-4.6.2
1个回答
0
投票

因为

string.Contains(string)
(即没有明确指定
StringComparison
)在 .NET 的最新实现中是一个非常特殊的野兽,看起来像下面这样

public bool Contains(string value) { if (value == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); return SpanHelpers.IndexOf( ref _firstChar, Length, ref value._firstChar, value.Length) >= 0; }

string.Contains(string, StringComparison)

 重载时,只需调用 
IndexOf
:

public bool Contains(string value, StringComparison comparisonType) { return IndexOf(value, comparisonType) >= 0; }

name.Contains(Separator, StringComparison.Ordinal)

 将产生“预期”的性能。

IndexOf

 用于序数比较,当前使用内部 
Ordinal.IndexOf
,其中包含针对更一般用例的大量检查和不同优化。

您可以尝试的一件事是使用

AsSpan().IndexOf

,它在我的机器上可以为优化版本带来更好的性能:

public string name { get; set; } = "qwerty"; private const string Separator = "::"; private char[] SeparatorChars = Separator.ToCharArray(); [Benchmark] public string GetSubName() { if (name.Contains(Separator)) { return name.Split(SeparatorChars)[0]; } return "INVALID"; } [Benchmark] public string GetSubNameOpt() { var index = name.AsSpan().IndexOf(SeparatorChars); if (index >= 0) { return name.AsSpan().Slice(0, index).ToString(); } return "INVALID"; }

方法意思是错误标准偏差获取子名称8.850 纳秒0.2388 纳秒0.3856 纳秒获取子名称选项8.430 纳秒0.2337 纳秒0.2400 纳秒
© www.soinside.com 2019 - 2024. All rights reserved.