我注意到了
string1.Length == string2.Length && string1 == string2
在处理大字符串时,比仅仅使用常规方法略微更快。
string1 == string2
这是真的吗?在比较实际字符串之前比较大字符串长度是否是一种好的做法?
我注意到了
string1.Length == string2.Length && string1 == string2
在处理大字符串时,比仅仅使用常规方法略微更快。
string1 == string2
这是真的吗?在比较实际字符串之前比较大字符串长度是否是一种好的做法?
string
的操作符“等于”会在比较字符之前进行长度检查。因此,使用此技巧并不能节省比较内容的操作。你可能仍然可以节省一些 CPU 周期,因为你的长度检查假设字符串不为 null,而 BCL 必须进行检查。所以,如果大多数情况下长度不相等,你将短路一些指令。
不过这里可能有误。也许操作符被内联并进行了优化。只有测量才能确定。(量化即真相)
如果你关心每一个循环周期,你应该考虑在第一次选择中使用不同的策略。也许受控代码甚至不是正确的选择。鉴于此,我建议使用较短的形式,并且不使用额外的检查。
string
操作符重载 ==
调用 String.Equals()
,这只会引发更多关于表现差异的疑问。 - Softerware==
比较字符串长度时,您也可以避免方法调用的开销。 - NetMage字符串等式运算符或 ==
内部调用 string.Equals
,因此请使用框架提供的string.Equals
或 ==
。它已经足够优化。
它首先比较引用,然后比较长度和实际字符。
您可以在这里找到源代码。
// 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);
}
}
==
操作符使用int i = strA.Length; if (i!= strB.Length) {return false;}
- Tim Schmelter==
操作符会比较对象的引用和值。C#与Java不同。 - jamesSampica我的测试结果
将10000个长度相同的字符串(长度为256)与10000个其他字符串进行比较
时间(s1 == s2):32536889个滴答声
时间(s1.Length == s2.Length)&&(s1 == s2):37380529个滴答声
将10000个字符串与10000个其他随机长度最大为256的字符串进行比较
时间(s1 == s2):27223517个滴答声
时间(s1.Length == s2.Length)&&(s1 == s2):23419529个滴答声
将10000个字符串与10000个其他字符串的随机长度在256到512之间进行比较
时间(s1 == s2):28904898个滴答声
时间(s1.Length == s2.Length)&&(s1 == s2):25442710个滴答声
令人难以置信的是,比较10000个等长字符串所需的时间比比较同样数量的更长数据还要长。
所有这些测试都是使用完全相同的数据进行的。
==
运算符定义如下: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只是将其渲染为==
),否则这将是一个无限递归的方法。
这意味着,在实际比较字符之前,==
已经检查了字符串的长度。
ILSPy
对我(.NET 4, 长度检查在 EqualsHelper
)的输出结果: public static bool Equals(string a, string b)
{
return a == b || (a != null && b != null && string.EqualsHelper(a, b));
}
- Tim Schmelterreturn (value != null || this == null) && string.EqualsHelper(this, value);
。 - p.s.w.gSystem.String::get_Length()
的调用。是的,我在EqualsHelper
内部还看到了另一个长度检查。 - p.s.w.g正如我承诺的那样,我写了一个带有秒表的简短代码 - 你可以复制粘贴它并在不同的字符串上尝试,看看差异
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;
}
}
我的结论:
我认为第一个更快的结果是由于string1.Length == string2.Length
为false。由于短路评估(SCE),实际字符串比较未进行,这可能会节省时间。
然而,如果字符串相等,则第一个更慢,因为它将首先检查长度,然后执行与第二个相同的操作。
有关&&
运算符和SCE的信息,请参见http://msdn.microsoft.com/en-us/library/2a723cdk.aspx。
string.Compare
来比较字符串本身。我通过这样做几乎获得了50%的性能提升。if (str1.Length == str2.Length)
{
if (string.Compare(str1, str2, StringComparison.Ordinal) == 0)
{
doSomething()
}
}
==
运算符也会先比较字符串长度。 - Tim Schmelter
string1 == string2
没有先检查长度呢? - Conrad Frix