智能感知缓慢故障排除技巧

Andy Rich 嗨,我的名字是 安迪·里奇 我是C++前端编译器的QA。VisualStudio2010中的IntelliSense系统具有更强大的功能、灵活性和准确性,但这些改进是以更大的复杂性为代价的。本文的目标是帮助您排除这个复杂系统的故障,并让您了解它是如何工作的(以及当它不工作时该怎么做)。

null

问题通常是PCHs

我花了大量时间帮助那些智能感知速度慢的客户,发现他们的性能问题几乎总是与PCH被禁用有关。对于大型C++翻译单元(以及大多数你关心的大单位),智能感知PCH对于确保快速智能化至关重要。正确的PCH设置对于快速构建也是至关重要的,因此正确的PCH设置有可能在两个方面带来好处。我以前写过一篇关于PCH模型以及如何在IDE中配置它的博文: http://blogs.msdn.com/vcblog/archive/2010/01/26/precompiled-header-files-in-visual-studio-2010.aspx . 这篇博文的重点是当你遵循了这些步骤,但事情仍然不适合你的时候该怎么办。

从错误窗口开始

在VS2010 RTM中,PCH中的错误将阻止IntelliSense编译器创建PCH。这是我们在SP1中已经解决的问题,但即使如此,错误窗口仍然是开始研究性能问题的好地方。

VS2010的一个新特性是C++的“红色拐点”——这些诊断是由智能感知编译器提供的。这些相同的诊断也提供给错误列表窗口。如果这个窗口不可见,您可以使用View->Error List或热键chord“Ctrl+,E”打开它。在这种情况下,您应该在头文件中明确查找错误,并从Error List窗口的顶部开始。使用VS2010 RTM,任何错误(甚至是编译器通常可以从中恢复的错误)都将阻止PCH的生成,并导致严重的IntelliSense缓慢(这在SP1中得到了解决,我将在后面的一节中讨论。)

Start with the error window

IntelliSense编译器不是生成编译器

在这里,重要的是指出IntelliSense编译器不同于build编译器。我们已尽一切努力使这两个编译器具有同等地位(有关C++如何使用C++CLI的更多信息,请检查此 博客文章 .)

但是,仍然存在差异,有时,使用我们的构建编译器编译而没有错误的文件将无法使用我们的IntelliSense编译器正确编译。通常,这是因为智能编译器对C++标准的解释比编译编译器更严格。在这些情况下,通常可以通过修复IntelliSense编译器报告的错误来解决此问题(在大多数情况下,构建编译器很乐意接受IntelliSense编译器所要求的更一致的代码。)

此外,如果您针对的是x86以外的体系结构,您可能会注意到IntelliSense编译器总是在x86模式下运行。这会产生很难处理的错误,虽然这些错误不会阻止您处理大多数代码,但它们会导致PCH生成失败,如上所述。

如果您找不到解决问题的代码解决方法,那么还有一个权宜之计可以帮助您:编译器宏 __智能感知__ ,仅在使用IntelliSense编译器时定义。您可以使用此宏来保护IntelliSense编译器不理解的代码,或者使用它在生成和IntelliSense编译器之间切换。

背景很重要

这是一个在我们的智能感知引擎中讨论上下文的好机会。IntelliSense引擎通过始终对正在编译的源文件拥有尽可能正确的视图来提供准确的结果。对于.cpp文件来说,这是相当简单的:这些文件是本机编译的,编译器可以理解它们。但是,对于.h文件,情况就不那么清楚了,因为这些文件只在关联的.cpp文件的上下文中编译。

在以前的VisualC++中,头文件只由智能分析程序解析,并且在NCB中包含一次,基于它们编译的单个上下文。安 吉姆·斯普林菲尔德的旧邮报 更详细地讨论这个所谓的“多模态”问题。我们在VisualC++ 2010中解决这个问题,通过在当前.CPP文件的上下文中编译所有的头文件,这样这个高度上下文的信息可以更准确。

但是,当.h文件在编辑器中处于活动状态时,IntelliSense引擎的适当资源是什么?它无法单独编译.h文件—这不是正确的上下文。.h文件几乎肯定包含在多个.cpp文件中—应该编译哪一个文件来获得正确的.h文件上下文?

在Visual C++ 2010中,我们介绍了一种称为包含图的技术。对于一个.h文件,这允许我们直接或间接地知道包含该.h文件的所有.cpp文件。这为我们提供了.h文件的所有可能上下文,但是我们仍然不知道您想要哪个.cpp文件。

理想情况下,这是可以由用户配置的,但是对于IntelliSense来说,这似乎是很重的。我们决定查看您最近使用的.cpp文件(由中的“TU cache”设置控制) 工具>选项>文本编辑器> > C/C++ >高级 )并查看include图是否报告了其中任何一个作为.h文件的有效上下文。如果是的话,我们就用这个上下文。如果没有这样的上下文可用,我们必须为.h文件选择任意上下文。

验证正在生成PCH

让我们回到诊断智能感知问题上来。假设您的头文件没有IntelliSense错误,我们应该检查是否正在生成PCH。实现这一点最简单的方法是在硬盘上查找iPCH文件,以确保它正在生成。

浏览到您的解决方案目录,并在下面找到“ipch”目录。在这里,您应该为每个项目找到一个目录。在这些目录中,“ipch”文件本身。查看文件的时间戳可以提供信息,但对我来说,肯定的证据是删除iPCH文件(您可能需要先关闭解决方案),并确保在.cpp文件上执行IntelliSense时重新创建iPCH文件。

如果您没有看到正在生成的iPCH文件,那么现在是返回并查看 PCH选项博客文章 并确保您的设置配置正确。

除非你用的是Makefile项目

一个巨大的警告是makefile项目。总的来说,makefile项目中的设置对于visualstudio项目系统是不透明的,因此扩展到IntelliSense系统。在这些情况下,include目录可能不正确,宏定义可能未设置,并且makefile中使用的任何编译器开关(包括那些控制PCH的开关!)不会打开。

对于这些情况,我们为makefile项目添加了一个额外的配置部分。右键单击项目,选择“属性”,然后转到“配置属性”->“NMake”。这里的“IntelliSense”小节用于特定于IntelliSense编译器的选项。这些选项将只传递给IntelliSense编译器,并且应该与传递给生成编译器的格式相同。理想情况下,应该根据makefile中使用的相同选项设置这些选项。特别是预处理器定义、include搜索路径和强制include对拥有正确的权限非常重要。当然,出于我们的目的,您还应该在“附加选项”部分中包含您的PCH选项。

作为PCH的快速而肮脏的解决方法,您通常可以指定“ /于 “没有参数,IntelliSense引擎将为您创建默认PCH。但从长远来看,如果在这里镜像构建系统的PCH设置,您的总体性能会更好,问题也会更少。

转到定义是一个非常特殊的情况

转到定义(GTD) 是我们的IntelliSense引擎执行的最复杂的操作之一,也是最常见的遭受IntelliSense减速的操作之一。Goto定义的一个大问题是,通常情况下,函数的定义不包含在当前正在解析的翻译单元中。这个 宣言 是编译器自然需要的,即.h文件中的原型,但提供此原型实现的.cpp文件通常不在当前TU中;通常,它甚至不在您当前的项目中(在某些情况下,它被隐藏在静态库或DLL中,并且不可能为定义编写实际的代码。)

在高层,Goto定义的实现方式如下:

  1. 为此类型生成限定名称(需要在GTD源点处发出QuickInfo请求)。
  2. 在浏览数据库中搜索与此限定名称匹配的所有定义。
  3. 对于找到的每个匹配定义,执行QuickInfo操作以查看目标限定名是否与源匹配。
  4. 如果找到匹配的定义,请停止(不要继续处理列表)。
  5. 如果找不到匹配的定义,请显示步骤2中的所有候选项。

通常需要很长时间的操作是第3步 preparse模型会对性能产生负面影响 . 步骤1通常不是问题,因为我们几乎总是为当前源文件生成preparse。然而,在第3步中,我们将看到一个新的、不相关的文件;而且这个文件通常没有生成preparse。决定这些操作速度的门控因素是preparse,而提高preparse速度的唯一好方法是PCH。因此,让您的PCH工作(如上所述)可能是最重要的事情,你可以做的性能。

使用任务管理器确定问题

有时,它可以帮助调出任务管理器,因为这将提供一些关于我们复杂的IntelliSense/浏览系统的哪一部分导致问题的见解。在执行长时间运行的Goto定义时,可以查看哪个进程正在消耗CPU周期。如果是devenv.exe,则问题更可能出现在浏览系统中(很可能是数据库查询)。这通常是由于您的解决方案的某种复杂性造成的,当您遇到它时,我们有兴趣进一步了解它。

如果你发现消耗资源的过程 vcpkgsrv.exe文件 ,那么问题就出在IntelliSense编译器上了——而且很可能是一个长时间运行的preparse(最好通过打开PCH并使其工作来解决)。

Visual C++ 2010中丰富的日志记录选项

Visual C++ 2010有一些额外的日志记录选项,可以帮助找出问题。要打开此日志记录,请转到 工具>选项>文本编辑器> > C/C++ >高级 并更改“下的选项” 诊断日志记录 ”. 要启用所有事件的完整日志记录(可以是大量日志记录数据),请将enable logging设置为True,logging Level设置为5,logging Filter设置为255,如下面的屏幕截图所示。

Rich Logging options in Visual C++ 2010

可以在输出窗口中查看此日志记录的输出(可能需要将“显示输出从:”下拉更改为“VisualC++日志”)。简单地说,我将介绍一个典型的QuickInfo操作的日志。

这是QuickInfo调用的日志:

[WorkItem]>>>[eNowQueue]类CQuickInfoWorkItem [WorkItem][eNowQueue]–执行类CQuickInfoWorkItem [IntelliSense]翻译单位:c:usersarichdocumentsvisual studio 2011项目示例u项目示例u项目示例u项目.cpp [WorkItem][eNowQueue]–类CQuickInfoWorkItem(1ms) [常规][UI]–类CQuickInfoWorkItem(1ms)

[03/15/2011@18:32:22.704]快速信息:成功:3ms:class MyClass

帮助理解IntelliSense操作的第一件事是注意“>>”字符,它表示IntelliSense引擎已将一个项放置在工作队列上。对于QuickInfo,工作称为“ CQuickInfoWorkItem项目 ”. 在这个场景中,您可以看到创建和排队QuickInfo工作项需要1毫秒,从队列中取出、处理和返回结果需要3毫秒(这几乎是瞬间发生的,因为这个翻译单元的准备工作已经完成了。)

IntelliSense日志中最有用的部分通常是查看IntelliSense编译器为满足您的IntelliSense请求而选择的翻译单元。如果IntelliSense编译器是第一次加载这个TU,那么您还将得到一个输出,指示编译这个文件时使用的命令行选项,这有时有助于诊断问题,特别是/Fp参数(它指示这个文件的ipch所在的位置)。

请注意,在Goto定义的情况下,由于编译器可能需要编译多个翻译单元才能提供答案,因此对于单个操作,您可能会看到多个“translation unit:”info语句(此外,如果启用了红色波形,则会看到由于导航到新的源文件而触发的其他工作项,以便检查编译错误。)

VisualStudio2010 SP1中的性能缓解措施

我们在VisualStudio2010SP1中添加了三个改进/缓解措施,旨在提供改进的IntelliSense体验。这些是:

  1. 改进了数据库查询,提高了类视图性能
  2. 长时间运行的Goto定义操作现在有一个cancel对话框
  3. 即使标头中有错误,也将创建IntelliSense PCH

在这些改进中,第三个应该给我们的用户带来最直接的好处。以前,我们只会创建一个iPCH文件,如果PCH头编译时没有任何错误。在某些情况下(特别是使用非x86代码),不可能完全无错误地获取PCH报头,从而导致非常差的IntelliSense性能,因为preparse无法利用PCH加速(这在Goto定义期间最为明显,因为“target”TU很可能之前没有创建preparse。)

有了这个特性,我们在仍然无法生成iPCH的情况下添加了一些特殊的诊断。这些主要是由于项目配置错误、文件丢失和其他灾难性错误造成的。错误的文本应该是“未生成IntelliSense PCH文件”。如果您在SP1中看到这些诊断,则几乎可以肯定,在诊断中的错误得到解决之前,您的IntelliSense性能会很差。

获取其他帮助

我总是有兴趣听到关于智力低下的具体反馈。我希望通过本文中提供的其他诊断信息,您可以帮助我们确定您遇到的性能问题、问题来自哪个组件,并更接近问题的实际根源。在connect bug中提供这些附加信息(尽可能多的可用信息)将有助于我们理解和解决这些问题。

谢谢!

安迪·里奇 Visual C++ QA

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享