【问题标题】:C# code very slow with debugger attached; MemoryMappedFile's fault?附加调试器的 C# 代码非常慢; MemoryMappedFiles 故障?
【发布时间】:2011-10-19 20:58:35
【问题描述】:

我有一个客户端/服务器应用程序。服务器组件运行,以“远程”方式使用 WCF(二进制格式化程序、会话对象)。

如果我启动服务器组件并启动客户端,服务器执行的第一个任务会在

如果我启动附加了 VS 调试器的服务器组件,然后启动客户端,则该任务需要 20 秒以上才能完成。

没有代码更改 - 没有条件编译更改。无论我是在 32 位、64 位、使用 VS 托管进程、没有 VS 托管进程或这些东西的任何组合中编译和运行服务器组件,都会发生同样的情况。

可能很重要:如果我使用 VS.NET profiler(采样模式),那么应用程序的运行速度就像没有附加调试器一样快。所以我不能这样诊断。刚刚检查,仪表模式也运行得很快。并发分析模式也一样,工作很快。

关键数据:

  • 该应用程序使用相当繁重的多线程(标准线程池中有 40 个线程)。无论如何,创建线程都会很快发生并且不是一个慢点。有很多锁,WaitHandles 和Monitor 模式
  • 该应用完全没有引发异常。
  • 应用程序不创建控制台输出。
  • 该应用完全是托管代码。
  • 该应用确实将磁盘上的一些文件映射到 MemoryMappedFile:1x750MB 和 12x8MB 以及一些较小的文件

实测效果:

  • 在这两种情况下,CPU 使用率都很低;附加调试器时,CPU 位于
  • 在这两种情况下,内存使用量都很小;在这两种情况下可能是 50 或 60MB
  • 发生了很多页面错误(参考 MMF),但是当附加调试器时它们发生得更慢
  • 如果不使用 VS 托管进程,或者基本上“远程调试监视器”开始发挥作用,那么 会使用大量 CPU 并产生大量页面错误。但这并不是问题发生的唯一一次
  • 无论客户端如何运行,都会出现性能差异。唯一更改的变量是通过“开始调试”运行的服务器组件与从资源管理器启动的服务器组件。

我的想法:

  • 调试时 WCF 速度慢?
  • MemoryMappedFiles 调试时速度慢?
  • 使用了 40 个线程 - 调试速度慢?也许监视器/锁通知调试器?线程调度变得奇怪/上下文切换非常罕见?
  • 宇宙背景辐射赋予 VS 智慧和残酷的幽默感

一切似乎都不太可能。

所以,我的问题:

  1. 为什么会这样?
  2. 如果#1 未知,我该如何诊断/找出?

【问题讨论】:

  • 您是否启用了第一次机会异常捕获?您还可以尝试启用 .NET Server Source Stepping 以在调试模式下捕获最多的底层“隐藏”异常,尤其是(反)serlization 异常。另外,跟踪(输出调试字符串或其他)呢?
  • 是的,根本不会抛出任何异常 - 所有类别的异常(包括 .NET)都启用了第一次机会。没有调试控制台输出(这就是我所说的控制台输出的意思——我将进行编辑以澄清)。我刚刚启用了 .NET Framework Source Stepping(看不到 Server Source Stepping).. 发现了一些异常。会及时更新
  • 来自 WCF 的异常:“' ' 字符,十六进制值 0x20,不能包含在名称中。”。我不知道异常可以以这种方式隐藏:异常不是异常吗?看看我能做些什么来解决。也许你可以发布一个答案,这样你就可以得到一些赞成/接受,如果这解决了它? :)
  • 您是否使用了条件断点?我已经看到这些减速工作非常有效。
  • @Paul - 不,没有断点!我认为 Simon 正在做一些事情,试图修复隐藏的异常,看看这是否能加快一切。

标签: c# performance debugging .net-4.0 memory-mapped-files


【解决方案1】:

由于这是在谷歌上搜索此问题时的第一个结果之一,我想在此处添加我的问题解决方案,希望像我的案例一样为某人节省 2 小时的研究时间。

我的代码从没有附加调试器的 30 秒减慢到使用调试器的 4 分钟。因为我忘了删除条件断点。这些似乎极大地减慢了执行速度,所以要小心那些

【讨论】:

    【解决方案2】:

    异常会显着影响应用程序的性能。有两种类型的异常:第一次机会异常(使用 try/catch 块优雅处理的异常)和未处理异常(最终会使应用程序崩溃)。

    默认情况下,调试器不显示第一次机会异常,它只显示未处理的异常。默认情况下,它还只显示代码中发生的异常。但是,即使它不显示它们,它仍然会处理它们,因此它的性能可能会受到影响(尤其是在负载测试或大循环运行中)。

    要在 Visual Studio 中启用第一次机会异常显示,请单击“调试|异常”以调用“异常”对话框,并在“公共语言运行时”部分选中“抛出”(您可以更具体并选择第一次机会你想看到的异常)。

    要启用来自应用程序任何位置(而不仅仅是来自您的代码)的第一次机会异常显示,请单击“工具 | 选项 | 调试 | 常规”并禁用“仅启用我的代码”选项。

    对于这些特定的“取证模式”情况,我还强烈建议启用 .NET Framework Source Stepping(它需要禁用“仅启用我的代码”)。了解正在发生的事情非常有用,有时仅查看调用堆栈就非常鼓舞人心 - 尤其是在宇宙辐射混合的情况下很有帮助:-)

    两篇相关的有趣文章:

    【讨论】:

    • 根据我的其他 cmets,异常是完全“隐藏的”,直到我启用了“.NET Framework Source Stepping”。有一个错误,SerializationInfo.SetValue() 抛出一个关于 string 参数不是有效的 XML 元素名称的异常,即使它会继续工作,而且,我使用的是 NetTcpBinding(即二进制格式化程序)。
    • 大多数方向在 VS2019 上不再存在。希望对此答案进行编辑:)
    • @Gulzar - 不确定如何继续,因为当您使用现代工具时,这种答案有点过时了,但不是每个人都这样做。加上一些信息仍然有效。通常,人们(像你一样 -:) 会查看日期并发现它已经过时,所以没什么大不了的。恕我直言,您应该写另一个问题或给出另一个答案。
    • @SimonMourier 实际上我只是在尝试此操作后才查看日期。泰伦的回答最终成为了解决方案。谢谢:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-22
    • 2018-06-13
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    • 2012-09-16
    相关资源
    最近更新 更多