0

String.EqualsHelper と String.CompareOrdinalHelper (.NET 2 & 4) の実装を見ると、比較では int バイト サイズが 2 で long バイト サイズが 4 であると想定しているように思えます。実際のサイズは 4 で、 .NET 32 ビットの世界では 8。私は何が欠けていますか?ここにキャッチはありますか?.NET フレームワークの EqualsHelper メソッドの内部http://www.netframeworkdevs.com/common-language-runtime/string-comparison-24141.shtmlのソース コード サンプル。

Reflector からリバース エンジニアリングされたソース (フレームワーク 2):

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
private static unsafe bool EqualsHelper(string strA, string strB)
{
    int length = strA.Length;
    if (length != strB.Length)
    {
        return false;
    }
    fixed (char* str = ((char*) strA))
    {
        char* chPtr = str;
        fixed (char* str2 = ((char*) strB))
        {
            char* chPtr2 = str2;
            char* chPtr3 = chPtr;
            char* chPtr4 = chPtr2;
            while (length >= 10)
            {
                if ((((*(((int*) chPtr3)) != *(((int*) chPtr4))) || (*(((int*) (chPtr3 + 2))) != *(((int*) (chPtr4 + 2))))) || ((*(((int*) (chPtr3 + 4))) != *(((int*) (chPtr4 + 4)))) || (*(((int*) (chPtr3 + 6))) != *(((int*) (chPtr4 + 6)))))) || (*(((int*) (chPtr3 + 8))) != *(((int*) (chPtr4 + 8)))))
                {
                    break;
                }
                chPtr3 += 10;
                chPtr4 += 10;
                length -= 10;
            }
            while (length > 0)
            {
                if (*(((int*) chPtr3)) != *(((int*) chPtr4)))
                {
                    break;
                }
                chPtr3 += 2;
                chPtr4 += 2;
                length -= 2;
            }
            return (length <= 0);
        }
    }
}

private static unsafe int CompareOrdinalHelper(string strA, string strB)
{
    int num = Math.Min(strA.Length, strB.Length);
    int num2 = -1;
    fixed (char* str = ((char*) strA))
    {
        char* chPtr = str;
        fixed (char* str2 = ((char*) strB))
        {
            char* chPtr2 = str2;
            char* chPtr3 = chPtr;
            char* chPtr4 = chPtr2;
            while (num >= 10)
            {
                if (*(((int*) chPtr3)) != *(((int*) chPtr4)))
                {
                    num2 = 0;
                    break;
                }
                if (*(((int*) (chPtr3 + 2))) != *(((int*) (chPtr4 + 2))))
                {
                    num2 = 2;
                    break;
                }
                if (*(((int*) (chPtr3 + 4))) != *(((int*) (chPtr4 + 4))))
                {
                    num2 = 4;
                    break;
                }
                if (*(((int*) (chPtr3 + 6))) != *(((int*) (chPtr4 + 6))))
                {
                    num2 = 6;
                    break;
                }
                if (*(((int*) (chPtr3 + 8))) != *(((int*) (chPtr4 + 8))))
                {
                    num2 = 8;
                    break;
                }
                chPtr3 += 10;
                chPtr4 += 10;
                num -= 10;
            }
            if (num2 == -1)
            {
                goto Label_0101;
            }
            chPtr3 += num2;
            chPtr4 += num2;
            int num3 = chPtr3[0] - chPtr4[0];
            if (num3 != 0)
            {
                return num3;
            }
            return (chPtr3[1] - chPtr4[1]);
        Label_00E7:
            if (*(((int*) chPtr3)) != *(((int*) chPtr4)))
            {
                goto Label_0105;
            }
            chPtr3 += 2;
            chPtr4 += 2;
            num -= 2;
        Label_0101:
            if (num > 0)
            {
                goto Label_00E7;
            }
        Label_0105:
            if (num > 0)
            {
                int num4 = chPtr3[0] - chPtr4[0];
                if (num4 != 0)
                {
                    return num4;
                }
                return (chPtr3[1] - chPtr4[1]);
            }
            return (strA.Length - strB.Length);
        }
    }
}
4

2 に答える 2

6
chPtr3 += 2;

これにより、ポインタが 4バイト、2文字分進みます。char は .NET では 2 バイトで、utf-16 エンコーディングで文字を格納します。char ポインタをインクリメントすると、1 char、2 バイトずつ進みます。

ここで実際に行われているのは、int* へのキャストです。これにより、アルゴリズムが高速になり、2 つの文字が同時に比較されます。これは 32 ビット CPU でうまく動作し、2 文字 (2 x 16 ビット = 32 ビット) を収めるのに十分な大きさのレジスタを備えています。ポインターを 10 文字進めるコードの部分は、ループを高速化するための一般的な最適化戦略であるループ展開に相当します。

それ以外の場合、アルゴリズムは、1 つの命令で同時に 4 文字を比較できる 64 ビット CPU 用に最適化されていません。必然的に、これは依然としてマネージ コードであるため、実行する CPU の種類に応じて、32 ビットまたは 64 ビットのマシン コードにコンパイルできます。

于 2012-04-08T19:10:36.990 に答える