.NET 6 对 StackOverflow 的优化

  Intro

  去年写了一系列的傻逼代码, 其中有一篇 写了多年代码,你会 StackOverflow 吗,昨天一不小心又写了一个 StackOverflow 代码。。然后想把新的代码加到原来 StackOverflow 的示例中,把原来的示例项目改成了 .NET 6 项目,偶然间发现 .NET 6 对于 StackOverlow 有一些小优化,且看下文

  Sample

  首先来看我们的 StackOverflow 的示例代码,最初的版本是没有 stack overflow 的,后来想着优化一下哎就改成了 StackOverflow 的版本了。。

  下面示例中有两个参数,可以理解为方法参数,会是动态的参数,不是一成不变的

  var isNegative = false;

  var oper = '>';

  第一个版本的代码是这样的:

  Func filter;

  if (!isNegative)

  {

  filter = oper == '>'

  ? (i => i > 0)

  : (i => i < 0);

  }

  else

  {

  filter = oper == '>'

  ? (i => i <= 0)

  : (i => i >= 0);

  }

  大概意图是这样的,isNegative 是表示条件是否取反,true 是 false 的反向操作,比如上面的代码,如果 isNegative 为 false,filter 表示 >0,则 isNegative为 true , filter 表示为 <=0

  第二次看这样的代码的时候,感觉是不是可以优化一下,如果 isNegative 为 true 的时候取一个反,于是改成了下面这样

  第二版:

  filter = oper == '>'

  ? (i => i < 0)

  : (i => i > 0);

  if (isNegative)

  {

  filter = i => !filter(i);

  }

  这样看起来是不是简单一些,但就是这样的代码会导致 StackOverflow

  上面的代码 filter=i=> !filter(i);等同于下面这样的代码:

  Func filter = x => x>0;

  filter = delegate (int i) =>

  {

  return filter(i);

  };

  如果觉得没有什么问题,我们可以再往下看,将上面的代码使用 ILSpy 使用 C# 1.0 翻译一下,可以看到翻译后的结果如下:

  <>c__DisplayClass4_0 <>c__DisplayClass4_ = new <>c__DisplayClass4_0();

  <>c__DisplayClass4_.func = <>c.<>9__4_0 (<>c.<>9__4_0 = new Func(<>c.<>9.

b__4_0));

  <>c__DisplayClass4_.func = new Func(<>c__DisplayClass4_.

b__1);

  private sealed class <>c

  {

  public static readonly <>c <>9 = new <>c();

  public static Func <>9__4_0;

  internal bool

b__4_0(int x)

  {

  return x > 0;

  }

  }

  private sealed class <>c__DisplayClass4_0

  {

  public Func func;

  internal bool

b__1(int x)

  {

  return func(x);

  }

  }

  从上面的代码可以看出来,这里发生了一个死循环,<>c__DisplayClass4_0 的 func 在调用的时候会调用

b__1 方法,而这个方法会再次调用 func 这个委托,之后互相调用起来最后就爆栈了...

  那么上面的代码是不是可以优化呢,可能也谈不上优化,只是用了模式匹配换了一种写法罢了,第三版写法如下:

  Func filter = (isNegative, oper) switch

  {

  (false, '>') => i => i > 0,

  (false, _) => i => i < 0,

  (true, '>') => i => i <= 0,

  (true, _) => i => i >= 0,

  };

  这里使用了 switch 的模式匹配来简化代码,效果和第一种方式完全一样,只是换了一种写法

  StackOverflow enhancement

  我们拿前面第二种写法的一个最简化的代码来做一个测试,代码如下:

  Func filter = x => x>0;

  filter = delegate (int i) =>

  {

  return filter(i);

  };

  Console.WriteLine(filter(10));

  首先把项目修改成 .netcoreapp3.1,然后运行这段代码,输出结果如下:

  

.NET 6 对 StackOverflow 的优化

  只有一句 Stack overflow.

  然后将项目改成 .net6.0 再运行,输出结果如下:

  

.NET 6 对 StackOverflow 的优化

  可以看到在 .NET 6 下,StackOverflow 的时候会打印出一个重复的次数以及调用的堆栈信息,这对于我们我们排查问题来说会非常的友好,可以让我们更快更准确的找到问题代码,减少焦虑的时间

  More

  在之前的版本中,如果发生了 StackOverflow 我们需要依赖 Dump 去分析调用堆栈,即使现在微软的文档中还有根据 dump 分析 StackOverflowException 的,这一优化可以帮助我们很好很高效的找到发生错误的代码

  References

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值