c# - 这段代码需要太长时间的原因是什么?

标签 c#

试图解决另一个 SO question ,我想出了以下我认为非常优化的算法。但是在运行时 DotNetBenchmark在所有解决方案中,令我感到非常惊讶的是我的代码运行在 387 ms 的惊人平均值上与 ~ 20-30 ms 相比其他一些答案已实现。

[MethodImpl(MethodImplOptions.AggressiveInlining)]
int CalcMe(string input) // I used Marc Gravel's input generation method
{
  var operands = input.Split(' ');
  var j = 1; // operators index

  var result = int.Parse(operands[0]); // output

  // i = numbers index
  for (int i = 2; i < operands.Length; i += 2)
  {
    switch (operands[j])
    {
      case "+":
        result += int.Parse(operands[i]);
        break;
      case "-":
        result -= int.Parse(operands[i]);
        break;
      case "*":
        result *= int.Parse(operands[i]);
        break;
      case "/":
        try
        {
          result /= int.Parse(operands[i]);
          break;
        }
        catch
        {
          break; // division by 0.
        }

      default:
        throw new Exception("Unknown Operator");
    }

    j += 2; // next operator
  }

  return result;
}

只需提取 String.Split()给来电者 Main()方法,我将执行降低到 110 ms ,但这仍然没有解开谜团,因为所有其他答案都直接处理输入。

我只是想了解也许可以改变我对优化的思考方式。我看不到任何我只使用的关键字。 switch , forint.Parse()几乎所有其他解决方案。

编辑 1:测试输入生成 输入生成是从 Marc 对原始问题的回答复制而来的,如下所示:

static string GenerateInput()
{
  Random rand = new Random(12345);
  StringBuilder input = new StringBuilder();
  string operators = "+-*/";
  var lastOperator = '+';
  for (int i = 0; i < 1000000; i++)
  {
    var @operator = operators[rand.Next(0, 4)];
    input.Append(rand.Next(lastOperator == '/' ? 1 : 0, 100) + " " + @operator + " ");
    lastOperator = @operator;
  }
  input.Append(rand.Next(0, 100));
  return input.ToString();
}

最佳答案

[MethodImpl(MethodImplOptions.AggressiveInlining)]

在这里几乎什么也做不了。当您想告诉编译器只需将代码复制并粘贴到多个地方以避免不必要的方法调用时,就会使用内联。在大多数情况下,知道什么时候自己做是非常聪明的。

var operands = input.Split(' ');

导致 JIT 遍历整个字符串、进行搜索、拆分字符串并填充数组,这可能需要很长时间。

开关(操作数[j])

在字符串上切换也会产生影响,因为它必须在 case 上调用 equals。如果您关注性能(例如 char),您会希望在 switch 中使用简单类型。

int.Parse

这实际上做了一堆分配,甚至处理了不安全的代码。您可以在此处查看解析代码:

https://referencesource.microsoft.com/#mscorlib/system/number.cs,698

或者如果链接断开:

[System.Security.SecuritySafeCritical]  // auto-generated
internal unsafe static Int32 ParseInt32(String s, NumberStyles style, NumberFormatInfo info) {

    Byte * numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
    NumberBuffer number = new NumberBuffer(numberBufferBytes);
    Int32 i = 0;

    StringToNumber(s, style, ref number, info, false);

    if ((style & NumberStyles.AllowHexSpecifier) != 0) {
        if (!HexNumberToInt32(ref number, ref i)) { 
            throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
        }
    }
    else {
        if (!NumberToInt32(ref number, ref i)) {
            throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
        }
    }
    return i;           
}

[System.Security.SecuritySafeCritical]  // auto-generated
private unsafe static void StringToNumber(String str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, Boolean parseDecimal) {

    if (str == null) {
        throw new ArgumentNullException("String");
    }
    Contract.EndContractBlock();
    Contract.Assert(info != null, "");
    fixed (char* stringPointer = str) {
        char * p = stringPointer;
        if (!ParseNumber(ref p, options, ref number, null, info , parseDecimal) 
                || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) {
            throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
        }
    }
}

关于c# - 这段代码需要太长时间的原因是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49534226/

相关文章:

c# - 是否所有的局部变量都入栈

c# - Microsoft jet 跳过 90 条记录

c# - 异步调用永远不会在 Asp.Net MVC 中返回

C# 舍入因平台而异?

c# - 嵌套的 Using 语句有用吗?

c# - 在 C# 中将 Action 委托(delegate)作为参数传递

c# - 手动处理 WinForms 控件的滚动

c# - 字符串到 float 与双重转换

c# - Web 服务器获取客户端机器的 MAC 地址

c# - 调试 ASP.NET 无法使用前导 .. 退出顶级目录