c# 比较字符串的最快方法
本文关键字:方法 比较 字符串 | 更新日期: 2023-09-27 18:35:53
我注意到
string1.Length == string2.Length && string1 == string2
在大字符串上比
string1 == string2
这是真的吗?在比较实际字符串之前比较大字符串长度是否是一种好习惯?
string
s 运算符等于在比较字符之前进行长度检查。因此,您不会使用此技巧保存内容的比较。您可能仍会节省一些 CPU 周期,因为长度检查假定字符串不为 null,而 BCL 必须检查这一点。因此,如果大多数时间长度不相等,您将短路一些指令。
不过,我可能只是错了。也许操作员被内联并且检查被优化出来。谁知道呢?(测量的人。
如果你关心保存每个周期,你可以首先使用不同的策略。也许托管代码甚至不是正确的选择。鉴于此,我建议使用较短的形式,而不是使用额外的检查。
String.Equality 运算符或==
在内部调用string.Equals
,因此请使用框架提供的string.Equals
或==
。它已经足够优化了。
长度,然后比较实际字符。
您可以在此处找到源代码
代码:(来源:http://www.dotnetframework.org/default.aspx/4@0/4@0/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/clr/src/BCL/System/String@cs/1305376/String@cs)
// Determines whether two strings match.
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public override bool Equals(Object obj) {
if (this == null) //this is necessary to guard against reverse-pinvokes and
throw new NullReferenceException(); //other callers who do not use the callvirt instruction
String str = obj as String;
if (str == null)
return false;
if (Object.ReferenceEquals(this, obj))
return true;
return EqualsHelper(this, str);
}
和
[System.Security.SecuritySafeCritical] // auto-generated
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
private unsafe static bool EqualsHelper(String strA, String strB)
{
Contract.Requires(strA != null);
Contract.Requires(strB != null);
int length = strA.Length;
if (length != strB.Length) return false;
fixed (char* ap = &strA.m_firstChar) fixed (char* bp = &strB.m_firstChar)
{
char* a = ap;
char* b = bp;
// unroll the loop
#if AMD64
// for AMD64 bit platform we unroll by 12 and
// check 3 qword at a time. This is less code
// than the 32 bit case and is shorter
// pathlength
while (length >= 12)
{
if (*(long*)a != *(long*)b) break;
if (*(long*)(a+4) != *(long*)(b+4)) break;
if (*(long*)(a+8) != *(long*)(b+8)) break;
a += 12; b += 12; length -= 12;
}
#else
while (length >= 10)
{
if (*(int*)a != *(int*)b) break;
if (*(int*)(a+2) != *(int*)(b+2)) break;
if (*(int*)(a+4) != *(int*)(b+4)) break;
if (*(int*)(a+6) != *(int*)(b+6)) break;
if (*(int*)(a+8) != *(int*)(b+8)) break;
a += 10; b += 10; length -= 10;
}
#endif
// This depends on the fact that the String objects are
// always zero terminated and that the terminating zero is not included
// in the length. For odd string sizes, the last compare will include
// the zero terminator.
while (length > 0)
{
if (*(int*)a != *(int*)b) break;
a += 2; b += 2; length -= 2;
}
return (length <= 0);
}
}
对于我们中间的极客来说,这里有一个页面在对多种比较字符串的方法进行基准测试方面做得很好。
简而言之,最快的方法似乎是CompareOrdinal:
if (string.CompareOrdinal(stringsWeWantToSeeIfMatches[x], stringsWeAreComparingAgainst[x]) == 0)
{
//they're equal
}
第二种最佳方法似乎是使用字典或哈希集,并将"键"作为您要比较的字符串。
读起来很有趣。
我的测试结果
将 10000 个字符串与 10000 个长度相同的其他字符串进行比较 (256)
时间 (s1 == s2):32536889 个刻度
时间(s1.长度 == s2。长度) &&(s1 == s2):37380529刻度
将 10000 个字符串与 10000 个其他字符串进行比较 随机长度最大 256
时间 (s1 == s2):27223517刻度
时间(s1.长度 == s2。长度) &&(s1 == s2):23419529刻度
将 10000 个字符串与 10000 个其他字符串进行比较 随机长度 最小 256 最大 512
时间 (s1 == s2):28904898 个刻度
时间(s1.长度 == s2。长度) && (s1 == s2): 25442710 刻度
我发现令人难以置信的是,比较 10000 个等长字符串比比较相同数量的更大数据需要更长的时间。
所有这些测试都是使用完全相同的数据完成的。
根据 ILSpy,字符串==
运算符定义为:
public static bool operator ==(string a, string b)
{
return string.Equals(a, b);
}
这被定义为
public static bool Equals(string a, string b)
{
return a == b || (a != null && b != null && a.Length == b.Length && string.EqualsHelper(a, b));
}
我假设第一个a == b
实际上是一个引用相等性检查(ILSpy只是将其渲染为==
),否则这将是一个无限递归的方法。
这意味着==
在实际比较字符串的字符之前已经检查了字符串的长度。
在终止字符串中,只开始比较字符是有意义的,因为无论如何,如果不迭代所有字符,就无法计算字符串长度,并且比较可能会提前退出。
对于长度计数字符串,如果要测试按字节相等性,则应首先比较长度。 如果不检索长度,您甚至无法开始访问字符数据,因为长度可能为零。
如果你正在进行关系比较,知道长度不同并不能告诉你结果应该是积极的还是消极的。 在具有文化意识的比较中,相等的字符串并不意味着相等的长度。 因此,对于这两个,您只需要比较数据。
如果operator==(string, string)
只是委托给关系比较,你就不会期望它比较长度。 因此,在进行比较之前检查长度可能是一个好处。 但似乎框架确实从长度检查开始。
说第一个更快是string1.Length == string2.Length
是假的结果。由于短路评估(SCE),然后不会在字符串之间进行实际比较,这可能会节省您的时间。
但是,如果字符串相等,则第一个字符串会变慢,因为它将首先检查长度,然后执行与第二个字符串相同的操作。
有关&&
运算符和 SCE 的信息,请参阅 http://msdn.microsoft.com/en-us/library/2a723cdk.aspx。
正如我所承诺的那样,我用秒表编写了一个简短的代码 - 您可以复制粘贴它并尝试不同的字符串并查看差异
class Program
{
static void Main(string[] args)
{
string str1 = "put the first value";
string str2 = "put the second value";
CompareTwoStringsWithStopWatch(str1, str2); //Print the results.
}
private static void CompareTwoStringsWithStopWatch(string str1, string str2)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 99999999; i++)
{
if (str1.Length == str2.Length && str1 == str2)
{
SomeOperation();
}
}
stopwatch.Stop();
Console.WriteLine("{0}. Time: {1}", "Result for: str1.Length == str2.Length && str1 == str2", stopwatch.Elapsed);
stopwatch.Reset();
stopwatch.Start();
for (int i = 0; i < 99999999; i++)
{
if (str1 == str2)
{
SomeOperation();
}
}
stopwatch.Stop();
Console.WriteLine("{0}. Time: {1}", "Result for: str1 == str2", stopwatch.Elapsed);
}
private static int SomeOperation()
{
var value = 500;
value += 5;
return value - 300;
}
}
我的结论:
- 当我检查一些字符串(短字符串和长字符串)时,我发现所有结果几乎相同。所以第一个 if(带有长度检查)在 2/3 中较慢。
- 并且在 Object 类中有一个 Equals 方法,只需使用它:)
- 您可以尝试一下,也可以给我们结果:)
如果您预计字符串在大多数时候的长度不同,您可以比较它们的长度,然后使用 string.Compare
比较字符串本身。通过这样做,我获得了近 50% 的性能提升:
if (str1.Length == str2.Length)
{
if (string.Compare(str1, str2, StringComparison.Ordinal) == 0)
{
doSomething()
}
}
在这种情况下,我希望字符串几乎一直都不同,我认为 str1。Lenght 比比较实际字符串便宜得多。如果它们的大小相等,我会比较它们。
编辑:忘记我说的话。只要使用==
,就开心了。