web-dev-qa-db-ja.com

C#文字列を比較する最速の方法

私はそれに気づいた

string1.Length == string2.Length && string1 == string2

大きな文字列では、単なる文字列よりもわずかに高速です

string1 == string2

これは本当ですか?そして、これは実際の文字列を比較する前に長い文字列の長さを比較する良い習慣ですか?

19
CoolCodeBro

strings演算子は、文字を比較する前に長さをチェックします。したがって、このトリックで内容の比較を保存しません。 mightは、長さのチェックで文字列がnullでないことを前提としているが、BCLはそれをチェックする必要があるため、依然として数CPUサイクルを節約します。そのため、ほとんどの場合、長さが等しくない場合、いくつかの命令を短絡します。

ただし、ここでは間違っているかもしれません。オペレーターがインライン化され、チェックが最適化される可能性があります。誰が確かに知っていますか? (測定する彼。)

すべてのサイクルを保存することに関心がある場合は、最初に別の戦略を使用する必要があります。マネージコードは正しい選択ではないかもしれません。そのため、追加のチェックを使用せず、短い形式を使用することをお勧めします。

19
usr

String.Equality Operator または==は内部で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);
    }
}
15
Habib

私のテスト結果

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個比較すると、同じ量のデータを比較するよりも時間がかかることです。

これらのテストはすべて、まったく同じデータで行われました。

7
user2888973

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は==)、それ以外の場合、これは無限に再帰的なメソッドになります。

この意味は ==は、実際に文字を比較する前に、文字列の長さをすでにチェックしています。

4
p.s.w.g

終了した文字列では、文字の比較を開始するのが理にかなっています。とにかくすべての文字を反復せずに文字列の長さを計算することはできず、比較は早期終了する可能性が高いからです。

長さを数える文字列では、長さの比較を最初に行う必要がありますバイト単位の等価性をテストしている場合。長さがゼロになる可能性があるため、長さを取得せずに文字データへのアクセスを開始することさえできません。

リレーショナル比較を行っている場合、長さが異なることを知っていても、結果が正か負かはわかりません。また、文化を意識した比較では、等しい文字列は等しい長さを意味しません。したがって、どちらの場合もデータを比較するだけです。

operator==(string, string)がリレーショナル比較に単純に委任する場合、長さを比較することは期待できません。したがって、比較を行う前に長さをチェックすることは有益です。しかし、フレームワークは長さのチェックから始まるようです。

3
Ben Voigt

私たちの間のオタクにとって、 ここにページがあります これはベンチマークで素晴らしい仕事をします 文字列を比較する多くの方法

一言で言えば、最速の方法はCompareOrdinalのようです。

if (string.CompareOrdinal(stringsWeWantToSeeIfMatches[x], stringsWeAreComparingAgainst[x]) == 0)
{
//they're equal
}

2番目の最良の方法は、比較する文字列として「キー」を使用した辞書またはハッシュセットを使用することです。

興味深い読み物になります。

1
Free Coder 24

ほとんどの場合、文字列の長さが異なることが予想される場合、長さを比較できます[〜#〜] and [〜#〜]その後、string.Compareを使用して文字列自体を比較できます。これにより、パフォーマンスがほぼ50%向上しました。

if (str1.Length == str2.Length)
{
    if (string.Compare(str1, str2, StringComparison.Ordinal) == 0)
    {
       doSomething()
    }
}

この場合、文字列はほとんど常に異なると予想されます。str1.Lenghtは実際の文字列を比較するよりもずっと安いと思います。それらのサイズが等しい場合、それらを比較します。

[〜#〜] edit [〜#〜]:私が言ったことは忘れてください。 ==を使用するだけで幸せになります。

0
Ricardo Pieper

最初の方が高速だと思いますが、string1.Length == string2.Lengthの結果はfalseです。短絡評価(SCE)のおかげで、ストリング間の実際の比較は行われず、時間を節約できます。

ただし、文字列が等しい場合、最初の文字列は最初に長さをチェックしてから2番目の文字列と同じことを行うため、低速です。

&&演算子とSCEの詳細については、 http://msdn.Microsoft.com/en-us/library/2a723cdk.aspx を参照してください。

0
JLe

ストップウォッチで短いコードを書いたと約束したので、コピーして貼り付け、異なる文字列で試して、違いを確認できます

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;
    }
}

私の結論:

  1. いくつかの文字列(短い文字列と長い文字列)を確認したところ、すべての結果がほぼ同じであることがわかりました。したがって、最初のif(長さチェック付き)は2/3で遅くなります。
  2. そして、ObjectクラスにEqualsメソッドがあり、それを使用するだけです:)
  3. あなたはそれを試して、私たちに結果を与えることもできます:)
0
Misha Zaslavsky