我正在玩,ReadOnlySpan并且我想自己看看它比使用字符串快得多,但是...到目前为止,还不是。我知道我可能在代码中犯了一个错误,但是找不到。
static int CountCharacterWithoutSpan(string originalString, string sequence)
{
int count = 0;
for (int i = 0, length = originalString.Length - sequence.Length; i < length; ++i)
{
if (originalString.Substring(i, sequence.Length).Equals(sequence))
{
count++;
}
}
return count;
}
static int CountCharacterWithSpan(ReadOnlySpan<char> originalString, string sequence)
{
int count = 0;
for (int i = 0, length = originalString.Length - sequence.Length; i < length; ++i)
{
if (originalString.Slice(i, sequence.Length).SequenceEqual(sequence))
{
count++;
}
}
return count;
}
Run Code Online (Sandbox Code Playgroud)
因此,基本上,此代码的目标是能够在另一个代码中找到一个字符串。两者之间的区别在于,我使用Slice代替Substring和SequenceEqual代替Equals。但是,当我使用来运行和监视此代码时Stopwatch,CountCharacterWithSpan总花费的时间要比CountCharacterWithoutSpan(字符串测试大约80K个字符)多2至3倍。
我认为问题出在这里,SequenceEquals但这是我发现比较切片ReadOnlySpan的字符串和常规字符串的唯一方法(Equals不起作用,==速度更快,但是比较引用,因此结果不正确)
与您在问题中所说的相反,基于跨度的版本实际上比非基于跨度的版本快得多。
\n\n根据评论中 morten-mertner 的建议,我对第二种方法做了稍微修改的版本:
\n\npublic static int CountCharacterWithSpan(\n ReadOnlySpan<char> originalString, ReadOnlySpan<char> sequence)\n{\n int count = 0;\n\n for (int i = 0, length = originalString.Length - sequence.Length; i < length; ++i)\n {\n if (originalString.Slice(i, sequence.Length).SequenceEqual(sequence))\n {\n count++;\n }\n }\n\n return count;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n但正如我们将看到的,这没有什么区别。它的速度与您原来的基于跨度的速度大约一样快,并且两者都比您的非基于跨度的速度快得多。
\n\n以下是 BenchmarkDotNet 对所有三个字符的报告,使用 80K 字符originalString和sequence在 .NET Core 2.2 上运行的 20 个字符,每个字符都有三种变体。在“随机”变体中,sequence只是随机文本,因此可以很早就检测到不存在匹配项。在“Match”变体中, 是sequence确实存在于文本中某处的子字符串,但输入仍然是随机的,因此大多数搜索很快终止,但其中一个会很慢。在“MatchAll”情况下, 和originalString都是sequence一遍又一遍地相同的字符,这意味着每次比较都会成功,这意味着可能进行最多的比较工作。(它需要一遍又一遍地比较每个字符。)
| Method | Mean | Error | StdDev |\n|---------------------------- |-----------:|-----------:|-----------:|\n| OriginalWithoutSpanRandom | 1,087.1 us | 11.4152 us | 10.6778 us |\n| OriginalWithoutSpanMatch | 1,098.8 us | 26.0405 us | 23.0842 us |\n| OriginalWithoutSpanMatchAll | 1,164.3 us | 15.8291 us | 14.8066 us |\n| OriginalWithSpanRandom | 188.8 us | 1.3194 us | 1.2341 us |\n| OriginalWithSpanMatch | 188.3 us | 0.6132 us | 0.5736 us |\n| OriginalWithSpanMatchAll | 224.3 us | 3.0027 us | 2.8087 us |\n| ModifiedWithSpanRandom | 189.0 us | 0.9979 us | 0.9334 us |\n| ModifiedWithSpanMatch | 189.5 us | 1.1694 us | 1.0367 us |\n| ModifiedWithSpanMatchAll | 223.2 us | 1.3251 us | 1.2395 us |\nRun Code Online (Sandbox Code Playgroud)\n\nsequence以下是更改为 200 个字符的结果:
| Method | Mean | Error | StdDev |\n|---------------------------- |-----------:|----------:|----------:|\n| OriginalWithoutSpanRandom | 2,432.2 us | 35.777 us | 31.715 us |\n| OriginalWithoutSpanMatch | 2,476.1 us | 42.809 us | 35.747 us |\n| OriginalWithoutSpanMatchAll | 2,815.6 us | 22.508 us | 19.953 us |\n| OriginalWithSpanRandom | 190.2 us | 1.531 us | 1.432 us |\n| OriginalWithSpanMatch | 189.8 us | 1.937 us | 1.717 us |\n| OriginalWithSpanMatchAll | 602.3 us | 4.662 us | 4.361 us |\n| ModifiedWithSpanRandom | 190.1 us | 2.200 us | 2.058 us |\n| ModifiedWithSpanMatch | 191.1 us | 2.860 us | 2.675 us |\n| ModifiedWithSpanMatchAll | 599.9 us | 3.696 us | 3.457 us |\nRun Code Online (Sandbox Code Playgroud)\n\nsequence如果我们更改为 2000 个字符,则结果如下:
| Method | Mean | Error | StdDev |\n|---------------------------- |------------:|-----------:|-----------:|\n| OriginalWithoutSpanRandom | 16,819.9 us | 310.576 us | 290.513 us |\n| OriginalWithoutSpanMatch | 17,148.8 us | 231.140 us | 216.209 us |\n| OriginalWithoutSpanMatchAll | 21,817.9 us | 246.378 us | 218.408 us |\n| OriginalWithSpanRandom | 184.2 us | 1.633 us | 1.528 us |\n| OriginalWithSpanMatch | 185.3 us | 1.440 us | 1.347 us |\n| OriginalWithSpanMatchAll | 4,649.7 us | 22.810 us | 20.221 us |\n| ModifiedWithSpanRandom | 185.2 us | 1.198 us | 1.120 us |\n| ModifiedWithSpanMatch | 186.7 us | 2.158 us | 2.019 us |\n| ModifiedWithSpanMatchAll | 4,651.1 us | 25.013 us | 22.173 us |\nRun Code Online (Sandbox Code Playgroud)\n\n正如你所看到的,我无法重现你所描述的结果,其中“CountCharacterWithSpan总是比”多花2到3倍CountCharacterWithoutSpan。在这些测试中,它CountCharacterWithoutSpan始终比ReadOnlySpan<char>基于 的版本慢得多。(但这两者之间的差异太小,无法衡量。)
对于这两种基于跨度的方法,每次比较中完成的工作量都是巨大的:您可以看到测试之间存在显着差异,其中大多数字符串比较可以在一两个字符后退出,而在测试中必须退出比较每个字符。Random(不过,示例和示例之间没有有意义的区别Match- 似乎让所有比较提前退出和让一个早期退出的成本差异很小。这并不奇怪,因为我们是基本上是8万个中比较一个是贵的,其余的是便宜的。
这里绝对清楚的是,非基于跨度的版本很昂贵。正是Substring对它的调用杀死了它。在大多数比较几乎立即失败的测试中,情况尤其糟糕:您分配了 的某个子字符串的 2,000 个字符副本originalString,然后只查看可能的少数字符。
请注意,在我们能够提前退出的情况下,基于跨度版本的性能几乎与长度无关sequence- 在所有情况下大约为 190us。这就是您所希望的\xe2\x80\x94,在我们可以确定很早就没有匹配项的情况下,它的长度并不重要sequence,但在您的非基于跨度的版本中,即使在这些情况下,时间长度也sequence很重要。
您在测试中进行了多少次测量?我想知道您是否只是测量单次运行,在这种情况下,您并没有真正测量代码运行所需的时间:您主要测量 JIT 编译器编译所需的时间它。
\n| 归档时间: |
|
| 查看次数: |
358 次 |
| 最近记录: |