当前位置:编程学习 > C#/ASP.NET >>

擂台:改进 String.CompareOrdinal 性能

--------------------编程问答--------------------             string s0 = "A quick brown fox jumps over a lazy jog. ";
--------------------编程问答-------------------- --------------------编程问答-------------------- 袁峰? 真的假的 --------------------编程问答-------------------- 我也姓袁,在学习c#,可能的话加下我QQ546541056 --------------------编程问答--------------------
static int CompareOrdinal_Leaf(string s1, string s2)
{
if (s1 == null && s2 == null)
return 0;
if (s1 == null)
return -1;
if (s2 == null)
return 1;
int min_len = s1.Length <= s2.Length ? s1.Length : s2.Length;
char[] a1 = s1.ToCharArray();
char[] a2 = s2.ToCharArray();
for (int i = 0; i < min_len; i++)
{
if (a1[i] > a2[i])
return 1;
if (a1[i] < a2[i])
return -1;
}
if (s1.Length > s2.Length)
return 1;
if (s1.Length < s2.Length)
return -1;
return 0;
}


自己写的函数,运行结果 (不用 native 好慢啊)
Short 12887.7371 ms -100000000
Long 63571.6361 ms -10000000
Total time 76459.3732 ms

用系统原生的函数,运行结果
Short 5321.3044 ms -100000000
Long 3575.2044 ms -10000000
Total time 8896.5088 ms
--------------------编程问答-------------------- 哈哈,楼上的太太慢了。重写。 --------------------编程问答-------------------- --------------------编程问答-------------------- Mark一下,回家写,大学的时候用Pascal时间实现过KMP... --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答--------------------
        unsafe static int MyCompare(string a, string b)
        {
            int pre, len, tmp;
            tmp = a.Length - b.Length;
            if (tmp > 0)
            {
                len = a.Length;
                pre = 1;
            }
            else if (tmp < 0)
            {
                len = b.Length;
                pre = -1;
            }
            else
            {
                len = a.Length;
                pre = 0;
            }
            fixed (char* pa = a)
            fixed (char* pb = b)
            {
                for (int i = 0; i < len; i++)
                {
                    int re = (int)(*pa) - (int)(*pb);
                    if (re != 0)
                        return re;
                }
            }
            return pre;
        }




String.CompareOrdinal
=====================
Short 3995.7456 ms -100000000
Long 4226.0768 ms -10000000
Total time 8221.8224 ms

MyCompare
=========
Short 2864.1184 ms -100000000
Long 11726.8624 ms -10000000
Total time 14590.9808 ms
--------------------编程问答-------------------- 上面代码作废,写完没检查。。。

下修正

        static int MyCompare(string a, string b)
        {
            int pre, len, tmp;
            tmp = a.Length - b.Length;
            if (tmp > 0)
            {
                len = b.Length;
                pre = 1;
            }
            else if (tmp < 0)
            {
                len = a.Length;
                pre = -1;
            }
            else
            {
                len = a.Length;
                pre = 0;
            }

            for (int i = 0; i < len; i++)
            {
                int re = (int)(a[i]) - (int)(b[i]);
                if (re != 0)
                    return re;
            }
            return pre;
        }


MyCompare
================
Short 2743.9456 ms -100000000
Long 21961.5792 ms -10000000
Total time 24705.5248 ms --------------------编程问答-------------------- 看来长的比较难 --------------------编程问答-------------------- 指针快一些

        unsafe static int MyCompareUnsafe(string a, string b)
        {
            int pre, len, tmp;
            tmp = a.Length - b.Length;
            if (tmp > 0)
            {
                len = b.Length;
                pre = 1;
            }
            else if (tmp < 0)
            {
                len = a.Length;
                pre = -1;
            }
            else
            {
                len = a.Length;
                pre = 0;
            }
            fixed (char* pa = a)
            fixed (char* pb = b)
            {
                for (int i = 0; i < len; i++)
                {
                    int re = (int)(*(pa + i)) - (int)(*(pb + i));
                    if (re != 0)
                        return re;
                }
            }
            return pre;
        }


MyCompareUnsafe
===================
Short 2824.0608 ms -100000000
Long 11566.632 ms -10000000
Total time 14390.6928 ms --------------------编程问答-------------------- 可以对N次进行比较结果进行缓存, 进而可将N次的比较时间, 简化成1次比较时间 + (N - 1) 循环累加时间 --------------------编程问答-------------------- 不可以缓存,这只是性能测试程序。

有没有人看过汇编指令? --------------------编程问答-------------------- mark 下,发现string.CompareOrdinalHelper 对长的比较速度非常惊人。 --------------------编程问答-------------------- mark下,回头来看。

不用Native Code有难度。 --------------------编程问答-------------------- --------------------编程问答-------------------- 看了一下CompareOrdinalHelper(string strA, string strB)的实现,真不是一般的复杂。
自己写的代码要慢很多了。 --------------------编程问答-------------------- --------------------编程问答--------------------

static unsafe int StringCompare2(string a, string b)
{
    fixed (char* pa = a, pb = b)
    {}
    return 1;
}

最快的方式 能想到的 就是用指针吧 
但是这样的一个程序

Short 1933.5937 ms 100000000
就花了这么多时间
LZ的程序才
Short 2364.2578 ms -100000000

只有400ms的时间能做什么?
Short 1933.5937 ms 100000000 --------------------编程问答-------------------- Short 3316.4063 ms -100000000
Long 4416.0156 ms -10000000

我已经是极限了...
为什么系统的快,因为系统的string类中有一个private 的char类型字段m_firstChar;
指示string的第一个char字符
系统使用fixed (char* chRef = &m_firstChar)方式获得指针      1940 ms
而我们需要使用fixed (char* chRef = (string)s1)方式获得指针  2270 ms
相差了300 ms的效率 而且是2个string 就相差了近 650 ms的效率

不过即使如此,就算减去650ms 我写的程序的效率也是没有系统高......




static int StringCompare2(string a, string b)
{
    int min_len = 0;
    try
    {
        min_len = a.Length > b.Length ? b.Length : a.Length;
    }
    catch
    {
        if (a == b)
            return 0;
        if (a != null)
            return 1;
        if (b != null)
            return -1;
    }

    unsafe
    {
        fixed (char* pa = a, pb = b)
        {
            int i = 0;
            if (min_len < 16)
            {
                while (min_len > i)
                {
                    if (pa[i] != pb[i])
                    {
                        return pa[i] > pa[i] ? 1 : -1;
                    }
                    i++;
                }
            }

            i = min_len - aaa(pa, pb, min_len);
            if (i == min_len)
            {
                if (min_len == b.Length)
                {
                    return min_len == a.Length ? 0 : 1;
                }
                return -1;
            }

            while (true)
            {
                if (pa[i] != pb[i])
                {
                    return pa[i] > pa[i] ? 1 : -1;
                }
                i++;
            }
        }
    }
    return 1;

    throw new Exception("不可能");
}
--------------------编程问答-------------------- 我的
Short 4071 ms -100000000
Long 3890 ms -10000000
系统的
Short 2310 ms -100000000
Long 1978 ms -10000000

好吧 我放弃了

static int StringCompare2(string a, string b)
{
    int min_len = 0;
    try
    {
        min_len = a.Length > b.Length ? b.Length : a.Length;
    }
    catch
    {
        if (a == b)
            return 0;
        if (a != null)
            return 1;
        if (b != null)
            return -1;
    }

    unsafe
    {
        fixed (char* pa = a, pb = b)
        {
            int i = min_len - aaa(pa, pb, min_len);

            if (i == min_len)
            {
                if (min_len == b.Length)
                {
                    return min_len == a.Length ? 0 : 1;
                }
                return -1;
            }

            while (true)
            {
                if (pa[i] != pb[i])
                {
                    return pa[i] > pa[i] ? 1 : -1;
                }
                i++;
            }
        }
    }
}


private static unsafe int aaa(char* pa, char* pb, int charCount)
{
    while (charCount >= 32)
    {
        if (*((long*)pa) != *((long*)pb))
        {
            return charCount;
        }
        if (*((long*)(pa + 4)) != *((long*)(pb + 4)))
        {
            return charCount;
        }
        if (*((long*)(pa + 8)) != *((long*)(pb + 8)))
        {
            return charCount;
        }
        if (*((long*)(pa + 12)) != *((long*)(pb + 12)))
        {
            return charCount;
        }
        if (*((long*)(pa + 16)) != *((long*)(pb + 16)))
        {
            return charCount;
        }
        if (*((long*)(pa + 20)) != *((long*)(pb + 20)))
        {
            return charCount;
        }
        if (*((long*)(pa + 24)) != *((long*)(pb + 24)))
        {
            return charCount;
        }
        if (*((long*)(pa + 28)) != *((long*)(pb + 28)))
        {
            return charCount;
        }
        pa += 32;
        pb += 32;
        charCount -= 32;
    }


    while (charCount >= 8)
    {
        if (*((long*)pa) != *((long*)pb))
        {
            return charCount;
        }
        if (*((long*)(pa + 4)) != *((long*)(pb + 4)))
        {
            return charCount;
        }
        pa += 8;
        pb += 8;
        charCount -= 8;
    }

    return charCount;
}

--------------------编程问答-------------------- 在Release模式下
我的胜一次 败一次
我的
Short 1786 ms -100000000
Long 1286 ms -10000000
系统的
Short 1311 ms -100000000
Long 1872 ms -10000000 --------------------编程问答-------------------- --------------------编程问答-------------------- static int CompareOrdinal_Leaf(string s1, string s2)
            {
                if (s1 == null && s2 == null)
                    return 0;
                if (s1 == null)
                    return -1;
                if (s2 == null)
                    return 1;
                int min_len = s1.Length <= s2.Length ? s1.Length : s2.Length;
                char[] a1 = s1.ToCharArray();
                char[] a2 = s2.ToCharArray();
                for (int i = 0; i < min_len; i++)
                {
                    if (a1[i] > a2[i])
                        return 1;
                    if (a1[i] < a2[i])
                        return -1;
                }
                if (s1.Length > s2.Length)
                    return 1;
                if (s1.Length < s2.Length)
                    return -1;
                return 0;
            }


--------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- if (*((long*)pa) != *((long*)pb))
        {
            return charCount;
        }

return charCount 是错的,应该看第一个字符的差。 --------------------编程问答-------------------- 哎,太复杂

--------------------编程问答-------------------- 居然可以用 unsafe 指针, 不是不能调用 native code 吗
--------------------编程问答-------------------- 哎,太复杂
--------------------编程问答--------------------
引用 34 楼 fengyuanmsft 的回复:
if (*((long*)pa) != *((long*)pb))
        {
            return charCount;
        }

return charCount 是错的,应该看第一个字符的差。


这个是4个char 一起判断,只要不对就说明其中有一个错了
然后再一个一个判断 --------------------编程问答--------------------
引用 36 楼 huwei001982 的回复:
居然可以用 unsafe 指针, 不是不能调用 native code 吗


不能用只能想超越系统的不可能的吧....

-----------------------------------------------------------

引用 34 楼 fengyuanmsft 的回复:
if (*((long*)pa) != *((long*)pb))
        {
            return charCount;
        }

return charCount 是错的,应该看第一个字符的差。


char:2个字节 16位
int:4个字节 32位
long:8字节  64位

改用long的指针,一次可以判断4和char,如果相同判断下4个
如果不同,返回当前位置
然后程序再进行逐位判断 --------------------编程问答-------------------- 我刚上论坛,大家以后多照顾! --------------------编程问答--------------------


--------------------编程问答-------------------- 第三版....
完整源码如下,使用Ctrl+F5,(不调试模式)运行,才能比系统的快,
调试模式就比系统的慢



static int StringCompare(string s1, string s2)
{
    return String.CompareOrdinal(s1, s2);
}

static int StringCompare2(string a, string b)
{
    int min_len = 0;
    try
    {
        min_len = a.Length > b.Length ? b.Length : a.Length;
    }
    catch
    {
        if (a == b)
            return 0;
        if (a != null)
            return 1;
        if (b != null)
            return -1;
    }
    unsafe
    {
        fixed (char* pa = a, pb = b)
        {
            int i = min_len < 8 ? 0 : min_len - aaa((long*)pa, (long*)pb, min_len);

            if (i != min_len)
            {
                while (i <= min_len)
                {
                    if (pa[i] != pb[i])
                    {
                        return pa[i] > pb[i] ? 1 : -1;
                    }
                    i++;
                }
            }

            if (min_len == b.Length)
            {
                return min_len == a.Length ? 0 : 1;
            }
            return -1;
        }
    }
}

private static unsafe int aaa(long* pa, long* pb, int charCount)
{
    while (charCount >= 32)
    {
        if (*pa != *pb)
        {
            return charCount;
        }
        if (*(pa + 1) != *(pb + 1))
        {
            charCount -= 4;
            return charCount;
        }
        if (*(pa + 2) != *(pb + 2))
        {
            charCount -= 8;
            return charCount;
        }
        if (*(pa + 3) != *(pb + 3))
        {
            charCount -= 12;
            return charCount;
        }
        if (*(pa + 4) != *(pb + 4))
        {
            charCount -= 16;
            return charCount;
        }
        if (*(pa + 5) != *(pb + 5))
        {
            charCount -= 20;
            return charCount;
        }
        if (*(pa + 6) != *(pb + 6))
        {
            charCount -= 24;
            return charCount;
        }
        if (*(pa + 7) != *(pb + 7))
        {
            charCount -= 28;
            return charCount;
        }
        pa += 8;
        pb += 8;
        charCount -= 32;
    }


    while (charCount >= 8)
    {
        if (*pa != *pb)
        {
            return charCount;
        }
        if (*(pa + 1) != *(pb + 1))
        {
            charCount -= 4;
            return charCount;
        }
        pa += 2;
        pb += 2;
        charCount -= 8;
    }

    return charCount;
}

static long CompareLoop(string s1, string s2, int count, out int result)
{
    result = 0;

    Stopwatch sw = new Stopwatch();
    sw.Start();

    for (int i = 0; i < count; i++)
    {
        result += StringCompare(s1, s2);
    }
    sw.Stop();
    return sw.ElapsedMilliseconds;
}

static long CompareLoop2(string s1, string s2, int count, out int result)
{
    result = 0;

    Stopwatch sw = new Stopwatch();
    sw.Start();

    for (int i = 0; i < count; i++)
    {
        result += StringCompare2(s1, s2);
    }
    sw.Stop();
    return sw.ElapsedMilliseconds;
}

static void Main(string[] args)
{

    //StringCompare2("aaaaa", "aaaab");

    string s0 = "A quick brown fox jumps over a lady jog. ";

    string s1 = String.Empty;

    for (int i = 0; i < 10; i++)
    {
        s1 = s1 + s0;
    }

    string s2 = s1 + "A";

    int count = 10 * 1000 * 1000;

    int result1;
    int result2;
    double t1;
    double t2;
    Console.WriteLine("系统方法");

    t1 = CompareLoop("b", "a", count * 10, out result1);
    t2 = CompareLoop(s1, s2, count, out result2);

    Console.WriteLine("Short {0} ms {1} ", t1, result1);
    Console.WriteLine("Long {0} ms {1} ", t2, result2);


    Console.WriteLine("自定义方法");
   
    t1 = CompareLoop2("b", "a", count * 10, out result1);
    t2 = CompareLoop2(s1, s2, count, out result2);

    Console.WriteLine("Short {0} ms {1} ", t1, result1);
    Console.WriteLine("Long {0} ms {1} ", t2, result2);

    Console.Read();
}
--------------------编程问答-------------------- 楼上的不就是反编译系统的 String.CompareOrdinal 代码吗。。。。。。。

这样都行?

题目要求不能用 unsafe  --------------------编程问答-------------------- Wow! Very nice post. I so love it.. 
<a href=http://www.popbrandcaps.com>monster hats</a>
monster hats
<a href="http://www.popbrandcaps.com" title="monster hats">monster hats</a>
[url=http://www.popbrandcaps.com monster hats] --------------------编程问答-------------------- 厉害啊 --------------------编程问答-------------------- 学习!!! --------------------编程问答--------------------

public static int CompareOrdinal(string strA, string strB)
{
    if (strA == strB)
    {
        return 0;
    }
    if (strA == null)
    {
        return -1;
    }
    if (strB == null)
    {
        return 1;
    }
    return CompareOrdinalHelper(strA, strB);
}

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




以上为源码
--------------------编程问答--------------------
引用 42 楼 jy02305022 的回复:
第三版....
完整源码如下,使用Ctrl+F5,(不调试模式)运行,才能比系统的快,


不错。不过短的应该可以更快。 --------------------编程问答-------------------- 差太远。不贴了。。 --------------------编程问答-------------------- --------------------编程问答--------------------
引用 43 楼 huwei001982 的回复:
楼上的不就是反编译系统的 String.CompareOrdinal 代码吗。。。。。。。

这样都行?

题目要求不能用 unsafe

系统的是这样的吗? --------------------编程问答--------------------
引用 47 楼 sjzlxd 的回复:
C# code

public static int CompareOrdinal(string strA, string strB)
{
    if (strA == strB)
    {
        return 0;
    }
    if (strA == null)
    {
        return -1;
    }
    if (strB == null)
 ……


这个才是系统的吧 --------------------编程问答-------------------- 可以用 unsafe, 不能调用 native code。


--------------------编程问答--------------------
引用 48 楼 fengyuanmsft 的回复:
引用 42 楼 jy02305022 的回复:
第三版....
完整源码如下,使用Ctrl+F5,(不调试模式)运行,才能比系统的快,


不错。不过短的应该可以更快。


这个不好意思了....昨天通宵了..现在有点迷糊了...实在看不出了 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答--------------------
引用 54 楼 jy02305022 的回复:
这个不好意思了....昨天通宵了..现在有点迷糊了...实在看不出了


在 unsafe 前加一句

if (min_len != 0)
{
   int diff = a[0] - b[0];

   if (diff != 0)
       return diff;
} --------------------编程问答--------------------
引用 58 楼 fengyuanmsft 的回复:
引用 54 楼 jy02305022 的回复:
这个不好意思了....昨天通宵了..现在有点迷糊了...实在看不出了


在 unsafe 前加一句

if (min_len != 0)
{
   int diff = a[0] - b[0];

   if (diff != 0)
       return diff;
}



=.=....这个优化太有针对性了吧,,,,万一来2个字符给你比较.... --------------------编程问答-------------------- 我到是想到一个,可以开多线程不......... --------------------编程问答--------------------
引用 59 楼 jy02305022 的回复:
这个优化太有针对性了吧,,,,万一来2个字符给你比较


假设你比较的是英文单词或中文字,第一个不同的可能性很大。 --------------------编程问答-------------------- --------------------编程问答-------------------- 很好用 --------------------编程问答-------------------- 来学习! --------------------编程问答-------------------- --------------------编程问答-------------------- study~ --------------------编程问答-------------------- 比这个能比出什么来?效率差别不在于这个 --------------------编程问答-------------------- 学习中,不错不错 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答--------------------
引用 67 楼 ktei2008 的回复:
比这个能比出什么来?效率差别不在于这个


厉害啊!!!! --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 呵呵。。。。。。。。。。用系统的 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 内容存入剪贴 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 这么长。。。 --------------------编程问答-------------------- 不是很明白呀。 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 牛逼!!!!!! --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 过来 学习下  --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答--------------------
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,