私はそれに気づいた
string1.Length == string2.Length && string1 == string2
大きな文字列では、単なる文字列よりもわずかに高速です
string1 == string2
これは本当ですか?そして、これは実際の文字列を比較する前に長い文字列の長さを比較する良い習慣ですか?
string
s演算子は、文字を比較する前に長さをチェックします。したがって、このトリックで内容の比較を保存しません。 mightは、長さのチェックで文字列がnullでないことを前提としているが、BCLはそれをチェックする必要があるため、依然として数CPUサイクルを節約します。そのため、ほとんどの場合、長さが等しくない場合、いくつかの命令を短絡します。
ただし、ここでは間違っているかもしれません。オペレーターがインライン化され、チェックが最適化される可能性があります。誰が確かに知っていますか? (測定する彼。)
すべてのサイクルを保存することに関心がある場合は、最初に別の戦略を使用する必要があります。マネージコードは正しい選択ではないかもしれません。そのため、追加のチェックを使用せず、短い形式を使用することをお勧めします。
String.Equality Operator または==
は内部で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);
}
}
私のテスト結果
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個比較すると、同じ量のデータを比較するよりも時間がかかることです。
これらのテストはすべて、まったく同じデータで行われました。
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)
がリレーショナル比較に単純に委任する場合、長さを比較することは期待できません。したがって、比較を行う前に長さをチェックすることは有益です。しかし、フレームワークは長さのチェックから始まるようです。
私たちの間のオタクにとって、 ここにページがあります これはベンチマークで素晴らしい仕事をします 文字列を比較する多くの方法 。
一言で言えば、最速の方法はCompareOrdinalのようです。
if (string.CompareOrdinal(stringsWeWantToSeeIfMatches[x], stringsWeAreComparingAgainst[x]) == 0)
{
//they're equal
}
2番目の最良の方法は、比較する文字列として「キー」を使用した辞書またはハッシュセットを使用することです。
興味深い読み物になります。
ほとんどの場合、文字列の長さが異なることが予想される場合、長さを比較できます[〜#〜] and [〜#〜]その後、string.Compare
を使用して文字列自体を比較できます。これにより、パフォーマンスがほぼ50%向上しました。
if (str1.Length == str2.Length)
{
if (string.Compare(str1, str2, StringComparison.Ordinal) == 0)
{
doSomething()
}
}
この場合、文字列はほとんど常に異なると予想されます。str1.Lenghtは実際の文字列を比較するよりもずっと安いと思います。それらのサイズが等しい場合、それらを比較します。
[〜#〜] edit [〜#〜]:私が言ったことは忘れてください。 ==
を使用するだけで幸せになります。
最初の方が高速だと思いますが、string1.Length == string2.Length
の結果はfalseです。短絡評価(SCE)のおかげで、ストリング間の実際の比較は行われず、時間を節約できます。
ただし、文字列が等しい場合、最初の文字列は最初に長さをチェックしてから2番目の文字列と同じことを行うため、低速です。
&&
演算子と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;
}
}
私の結論: