简体   繁体   English

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

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

I am trying to remove a method to optimize my code.我正在尝试删除一种优化我的代码的方法。 It seems I didn't gain any performance, moreover, the "optimized" code is slower?似乎我没有获得任何性能,而且“优化”的代码更慢? Is it faster to call a method than to create a variable in the loop?调用方法比在循环中创建变量更快吗? Why?为什么?

Why the following code is faster (1.3-1.5 seconds)为什么下面的代码更快(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
                ;
    }

}

than the "optimized" version (1.5-1.8 seconds)?比“优化”版本(1.5-1.8 秒)? What am I missing?我错过了什么?

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

I have run dozens of tests using我已经使用了几十个测试

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

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

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

PS As you see I removed some code from getPureText(), made it return void, then measured time again: the same result. PS 如您所见,我从 getPureText() 中删除了一些代码,使其返回 void,然后再次测量时间:相同的结果。 Something wrong is there...有什么不对劲...

PPS Configuration: Debug. PPS 配置:调试。

EDIT编辑

For peeledText replaced type string to StringBuilder .对于peeledText将类型string替换为StringBuilder

Configuration: release.配置:发布。

Size of the string is the same: 150 KB.字符串的大小相同:150 KB。

3 series of the testing for 500 iterations each. 3 个系列的测试,每个系列进行 500 次迭代。

  • With method isCyrillic code: 6.63-6.70 milliseconds使用方法isCyrillic代码:6.63-6.70 毫秒

  • Inlined: 6.80-6.90 milliseconds (still slower o_0)内联:6.80-6.90 毫秒(仍然较慢 o_0)

  • Inlined but using RegEx: 6.62-6.70 milliseconds内联但使用 RegEx:6.62-6.70 毫秒

  • With method isCyrillic but using HashSet instead of the switch : 7.89-8.32 milliseconds.使用方法isCyrillic但使用HashSet而不是switch :7.89-8.32 毫秒。

If the code is taking 1.6 seconds to run processing one single input string, then its a rather large one.如果代码需要 1.6 秒来运行处理一个输入字符串,那么它是一个相当大的字符串。 I'd stop using string concatenation ( += ) and start using a System.Text.StringBuilder , its probably faster and much more memory efficient:我将停止使用字符串连接+= )并开始使用System.Text.StringBuilder ,它可能更快并且 memory 效率更高:

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

If your are calling getPureText in a somewhat tight loop you might consider reusing buffer simply clearing it and avoiding the newing cost on each call.如果您在一个有点紧的循环中调用getPureText ,您可能会考虑重用buffer ,只需清除它并避免每次调用的新成本。

Benchmark it again in release mode, discarding the warm up run and without debugger attached .发布模式下再次对其进行基准测试,放弃预热运行并且没有附加调试器 If that still doesn't meet your performance goals then start micro optimizing inlining calls, etc. The jitter is pretty smart optimizing code so its probably not going to gain you much.如果这仍然不能满足您的性能目标,那么开始微优化内联调用等。抖动是非常聪明的优化代码,因此它可能不会给您带来太多好处。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM