时间:2011-10-28 23:40:01

标签: c# .net performance

这个简单的c#代码用于在HTML中查找脚本块,需要0.5秒才能在74K字符串上运行,其中只有9个脚本块。这是2.8Ghz i7 CPU上未发布的二进制文件。我通过此代码进行了多次运行,以确保性能不受JIT的阻碍。事实并非如此。

这是VS2010 .NET 4.0客户端配置文件。 64

为什么这么慢?

                int[] _exclStart = new int[100];
                int[] _exclStop = new int[100];
                int _excl = 0;
                for (int f = input.IndexOf("<script", 0); f != -1; )
                {
                    _exclStart[_excl] = f;
                    f = input.IndexOf("</script", f + 8);
                    if (f == -1)
                    {
                        _exclStop[_excl] = input.Length;
                        break;
                    }
                    _exclStop[_excl] = f;
                    f = input.IndexOf("<script", f + 8);
                    ++_excl;
                }

5 个答案:

答案 0 :(得分:17)

我使用此页面上的源代码作为示例,然后我将内容复制8次,导致页面长度为334,312字节。使用StringComparision.Ordinal会产生巨大的性能差异。

string newInput = string.Format("{0}{0}{0}{0}{0}{0}{0}{0}", input.Trim().ToLower());
//string newInput = input.Trim().ToLower();

System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
int[] _exclStart = new int[100];
int[] _exclStop = new int[100];
int _excl = 0;
for (int f = newInput.IndexOf("<script", 0, StringComparison.Ordinal); f != -1; )
{
    _exclStart[_excl] = f;
    f = newInput.IndexOf("</script", f + 8, StringComparison.Ordinal);
    if (f == -1)
    {
        _exclStop[_excl] = newInput.Length;
        break;
    }
    _exclStop[_excl] = f;
    f = newInput.IndexOf("<script", f + 8, StringComparison.Ordinal);
    ++_excl;
}
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalMilliseconds);

运行5次会产生几乎相同的结果(循环时间没有显着变化,因此对于这个简单的代码,JIT几乎没有时间来编译它)

使用原始代码输出(毫秒):

10.2786
11.4671
11.1066
10.6537
10.0723

使用上述代码输出(毫秒):

0.3055
0.2953
0.2972
0.3112
0.3347

请注意,我的测试结果大约是 0.010秒(原始代码)和 0.0003秒(对于序数代码)。这意味着除了这段代码之外你还有别的错误。

如果正如您所说,使用StringComparison.Ordinal对您的表现没有任何作用,则表示您使用不正确的计时器来计算您的表现,或者您在阅读input值时遇到很大的开销,例如再次从流中读取它,否则你没有意识到。

使用.NET 4 Client Profile在3GHz i5上运行的Windows 7 x64下进行测试。

建议:

  1. 使用StringComparison.Ordinal
  2. 确保您使用System.Diagnostics.Stopwatch时间表现
  3. input声明一个局部变量,而不是使用函数外部的值(例如:string newInput = input.Trim().ToLower();
  4. 我再次强调,对于使用您提供的完全相同的代码显然超过4倍的测试数据,我的速度快50倍。这意味着我的测试比你的测试运行快200倍,这不是任何人都会期望的,因为我们都运行相同的环境而只是i5(我)和i7(你)。

答案 1 :(得分:4)

您正在使用的IndexOf重载是文化敏感的,这会影响性能。相反,使用:

input.IndexOf("<script", 0, StringComparison.Ordinal); 

答案 2 :(得分:3)

我建议使用RegEx,因为表达式只编译一次,所以它提供了显着的性能提升。而IndexOf本质上是一个基于每个字符运行的循环,这可能意味着,你的主循环中有3个“循环”,当然,IndexOf不会像常规循环一样慢,但是当输入大小增加时时间增加。 Regex具有内置函数,可以返回您定义的每个模式的出现次数和位置。

编辑:这可能会对IndexOf IndexOf Perf

的表现有所了解

答案 3 :(得分:2)

我只是在Windows 7上使用.NET 4.0测试IndexOf性能

public void Test()
{
    var input = "Hello world, I'm ekk. This is test string";

    TestStringIndexOfPerformance(input, StringComparison.CurrentCulture);
    TestStringIndexOfPerformance(input, StringComparison.InvariantCulture);
    TestStringIndexOfPerformance(input, StringComparison.Ordinal);

    Console.ReadLine();
}

private static void TestStringIndexOfPerformance(string input, StringComparison stringComparison)
{
    var count = 0;
    var startTime = DateTime.UtcNow;
    TimeSpan result;

    for (var index = 0; index != 1000000; index++)
    {
        count = input.IndexOf("<script", 0, stringComparison);
    }

    result = DateTime.UtcNow.Subtract(startTime);

    Console.WriteLine("{0}: {1}", stringComparison, count);
    Console.WriteLine("Total time: {0}", result.TotalMilliseconds);
    Console.WriteLine("--------------------------------");
}

结果是:

CurrentCulture:
    Total time: 225.4008

InvariantCulture:
    Total time: 187.2003

Ordinal:
    Total time: 124.8003

正如您所看到的,Ordinal的表现稍好一些。

答案 4 :(得分:1)

我不在这里讨论代码,可能会用Regex编写等等......但是为了我很慢,因为 IndexOf() * 在< / em> * for总是从头开始重新扫描字符串(它总是从索引0开始)尝试从最后发现的存储中扫描。