内联方法以提高性能,但在 C# 中执行速度较慢

Inlining a method to increase performance but it executes slower in C#

我正在尝试删除一种方法来优化我的代码。似乎我没有获得任何性能,而且,“优化”代码更慢!调用方法是否比在循环中创建变量更快?为什么?

为什么下面的代码更快(1.3-1.5秒)

public void getPureText(string notClearedText)
{
    string peeledText = "";


    foreach (var symbol in notClearedText)
    {
        if(isCyrillic(symbol))
            peeledText += Char.ToLower(symbol);
        else
            peeledText += " ";
    }
}

private bool isCyrillic(int letterCode)
{
    switch (letterCode)
    {
        case 1028: // Є
        case 1108: // є
        case 1030: // І
        case 1110: // і
        case 1031: // Ї
        case 1111: // ї
        case 1168: // Ґ
        case 1169: // ґ
        case 32:  // " "
        case 39:  // '
                  //case 45:  // -
            return true;
        default:
            return
                1040 <= letterCode && letterCode <= 1103 &&  // Cyrillic
                letterCode != 1066 &&  // Ъ
                letterCode != 1067 &&  // Ы
                letterCode != 1098  // ъ
                ||
                65 <= letterCode && letterCode <= 90
                ||
                97 <= letterCode && letterCode <= 122
                ;
    }

}

比“优化”版本(1.5-1.8 秒)?我错过了什么?

public void getPureText(string notClearedText)
{
    string peeledText = "";

    foreach (var symbol in notClearedText)
    {
      int letterCode = symbol;

      switch (letterCode)
      {
        case 1028: // Є
        case 1108: // є
        case 1030: // І
        case 1110: // і
        case 1031: // Ї
        case 1111: // ї
        case 1168: // Ґ
        case 1169: // ґ
        case 32:  // " "
        case 39:  // ' //case 45:  // -
            peeledText += Char.ToLower(symbol);
            break;
        default:
            if (
                1040 <= letterCode && letterCode <= 1103 && // Cyrillic
                letterCode != 1066 && // Ъ
                letterCode != 1067 && // Ы
                letterCode != 1098 // ъ
                ||
                65 <= letterCode && letterCode <= 90
                ||
                97 <= letterCode && letterCode <= 122
            )
                peeledText += Char.ToLower(symbol);
            else
                peeledText += " ";
            
            break;
         }
    }
}

我有 运行 几十个测试使用

void TestPerformance()
{
    Stopwatch sw = new Stopwatch();

    sw.Start();
    _textRepository.getPureText(RawTextExamples.veryLongText);
    sw.Stop();

    unitTestFormGuess.show(sw.Elapsed.ToString());
}

P.S。 如您所见,我从 getPureText() 中删除了一些代码,使其 return 无效,然后再次测量时间:相同的结果。有什么不对...

P.P.S。 配置:调试。

编辑

对于 peeledText 将类型 string 替换为 StringBuilder

配置:发布。

字符串的大小相同:150 KB。

3 系列测试,每系列 500 次迭代。

如果代码 运行 处理一个输入字符串需要 1.6 秒,那么它是一个相当大的字符串。我会 stop using string concatenation (+=) and start using a System.Text.StringBuilder,它可能更快,内存效率更高:

public void getPureText(string notClearedText)
{
    var peeledText = new StringBuilder(notClearedText.Length);

    foreach (var symbol in notClearedText)
    {
        if(isCyrillic(symbol))
            peeledText.Append(Char.ToLower(symbol));
        else
            peeledText.Append(' ');
    }
}

如果您在一个有点紧凑的循环中调用 getPureText,您可以考虑重用 buffer 简单地清除它并避免每次调用的新成本。

发布模式下再次对其进行基准测试,放弃预热 运行 并且不附加调试器 。如果仍然不能满足你的性能目标,那么开始微优化内联调用等。抖动是非常聪明的优化代码,所以它可能不会给你带来太多。

long Benchmark(string veryLongText, int repetitions)
{
    getPureText(veryLongText); //warmup
    var watch = Stopwatch.StartNew();

    for (var i = 0; i < repetitions; i++)
        getPureText(veryLongText);

    watch.Stop();
    return watch.ElapsedMilliseconds/repetitions;
}