使用附加的调试器时,C# 代码非常慢; MemoryMappedFile 的错?

发布于 12-10 17:42 字数 1296 浏览 1 评论 0原文

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

如果我启动服务器组件并启动客户端,服务器执行的第一个任务将在 <0.5 秒内完成。

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

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

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

关键数据:

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

测量的性能:

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

我的想法:

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

似乎不太可能。

所以,我的问题是:

  1. 为什么会发生这种情况?
  2. 如果#1 未知,我该如何诊断/找出答案?

I have a client/server app. The server component runs, uses WCF in a 'remoting' fashion (binary formatter, session objects).

If I start the server component and launch the client, the first task the server does completes in <0.5sec.

If I start the server component with VS debugger attached, and then launch the client, the task takes upwards of 20sec to complete.

There are no code changes - no conditional compilation changes. The same occurs whether I have the server component compiled and running in 32-bit, 64-bit, with the VS hosting process, without the VS hosting process, or any combination of those things.

Possibly important: If I use the VS.NET profiler (sampling mode), then the app runs as quick as if there were no debugger attached. So I can't diagnose it that way. Just checked, instrumentation mode also runs quickly. Same for the concurrency profiling mode, works quickly.

Key data:

  • The app uses fairly heavy multithreading (40 threads in the standard thread pool). Creating the threads happens quickly regardless and is not a slow point. There are many locks, WaitHandles and Monitor patterns
  • The app raises no exceptions at all.
  • The app creates no console output.
  • The app is entirely managed code.
  • The app does map a few files on disk to a MemoryMappedFile: 1x750MB and 12x8MB and a few smaller ones

Measured performance:

  • CPU use is minimal in both cases; when debugger is attached, CPU sits at <1%
  • Memory use is minimal in both cases; maybe 50 or 60MB in both cases
  • There are plenty of page faults happening (ref MMF), however they happen more slowly when the debugger is attached
  • If the VS hosting process is not used, or basically the 'remote debugging monitor' comes into play, then that uses a decent amount CPU and creates a good number of page faults. But that's not the only time the problem is occurring
  • The performance difference is seen regardless of how the client is run. The only variable being changed is the server component being run via 'Start with debugging' vs launched from Explorer.

My ideas:

  • WCF slow when debugged?
  • MemoryMappedFiles slow when debugged?
  • 40 threads used - slow to debug? Perhaps Monitors/locks notify debugger? Thread scheduling becomes strange/context switches very infrequent?
  • Cosmic background radiation granting intelligence and cruel sense of humour to VS

All seem stupidly unlikely.

So, my questions:

  1. Why is this happening?
  2. If #1 unknown, how can I diagnose / find out?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

内心旳酸楚2024-12-17 17:42:59

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

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

Since this is one of the first results when googling for this issue I would like to add my problem solution here in the hopes of saving someone 2 hours of research like in my case.

My code slowed down from 30 seconds without debugger attached to 4 minutes with debugger. because I forgot to remove a conditional breakpoint. These seem to slow down execution tremendously, so watch out for those

天赋异禀2024-12-17 17:42:59

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

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

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

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

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

两篇相关的有趣文章:

Exceptions can notably impact the performance of an application. There are two types of exceptions: 1st chance exceptions (the one gracefully handled with a try/catch block), and unhandled exceptions (that will eventually crash the application).

By default, the debugger does not show 1st chance exceptions, it just shows unhandled exceptions. And by default, it also shows only exceptions occurring in your code. However, even if it does not show them, it still handles them, so its performance may be impacted (especially in load tests, or big loop runs).

To enable 1st chance exceptions display in Visual Studio, click on "Debug | Exceptions" to invoke the Exceptions dialog, and check "Thrown" on the "Common language runtime" section (you can be more specific and choose wich 1st chance exception you want to see).

To enable 1st chance exceptions display originating from anywhere in the application, not just from your code, click on "Tools | Options | Debugging | General" and disable the "Enable Just My Code" option.

And for these specific "forensics mode" cases, I also strongly recommend to enable .NET Framework Source Stepping (it requires "Enable Just My Code" to be disabled). It's very useful to understand what's going on, sometimes just looking at the call stack is very inspiring - and helpful especially in the case of cosmic radiation mixup :-)

Two related interesting articles:

苦笑流年记忆2024-12-17 17:42:59

可能的原因:

  1. 各种特殊类型的断点例如:

    • 条件断点
    • 内存更改断点
    • 函数断点
  2. 选中“启用本机代码调试”选项。

    • 此选项使调试运行速度慢如糖蜜。
    • 此选项不在工具 -> 下选项->调试,(这太有意义了)它位于 Project -> 下属性->调试
  3. 过度使用System.Diagnostics.Debug.Write()

    • 我的基准测试显示,在不进行调试的情况下运行 1000 次 Debug.WriteLine() 调用只需要 10 毫秒,但在调试时则需要整整 500 毫秒。显然,Visual Studio 调试器在活动时会拦截 DotNet 调试输出,并用它执行极其耗时的操作。 (在使用 Microsoft 产品数十年的时间里,我们对 Microsoft 的期望丝毫不减。)
    • 用 kernel32.dll 替换 Debug.WriteLine() -> PInvoke-> OutputDebugStringW() 没有帮助,因为运行 DotNet 应用程序时,Visual Studio 完全忽略 kernel32 调试输出,只显示 DotNet 调试输出,这是完全不同的事情。 (而且我认为其他任何事情都太有意义了。)
  4. 正如另一个答案所暗示的那样,抛出和捕获的异常数量过多。

    • 在 DotNet 下抛出异常是一个极其缓慢的操作。
    • 在 DotNet 下收集堆栈跟踪是一项极其缓慢的操作。

Possible causes:

  1. Various special kinds of breakpoints such as:

    • Conditional breakpoints
    • Memory changed breakpoints
    • Function breakpoints
  2. Having the "Enable native code debugging" option checked.

    • This option makes debug runs slow as molasses.
    • This option is not under Tools -> Options -> Debug, (that would make too much sense,) it is under Project -> Properties -> Debug
  3. Excessive use of System.Diagnostics.Debug.Write().

    • My benchmarks show that 1000 invocations of Debug.WriteLine() take only 10 milliseconds when running without debugging, but a whole 500 milliseconds when debugging. Apparently the Visual Studio Debugger, when active, intercepts DotNet debug output, and does extremely time-consuming stuff with it. (Over decades of using Microsoft products, we have come to expect nothing less from Microsoft.)
    • Replacing Debug.WriteLine() with kernel32.dll -> PInvoke -> OutputDebugStringW() does not help, because when running a DotNet application, Visual Studio completely ignores kernel32 debug output and only displays DotNet debug output, which is a completely different thing. (And I suppose that anything else would, again, make too much sense.)
  4. Excessive amount of exceptions being thrown and caught, as another answer suggests.

    • Throwing an exception under DotNet is a mind-bogglingly slow operation.
    • Collecting a stack trace under DotNet is an insanely slow operation.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文