预编译头(PCH)问题和建议

这篇文章是马克·霍尔写的, 襄樊 , 尤里索洛德基 , 巴特乌尔齐卢夫桑巴特 ,和 安德鲁·帕多 .

null

预编译头可以显著减少编译时间。他们已经为数百万的开发者提供了可靠的服务 自从25年前它们被引入来加速MFC应用程序的构建以来 . 预编译头文件被广泛使用:默认情况下,它们在IDE中创建的新VisualC++项目中是类似的,并且类似地 在我们的intellisense架构中提供实质性的性能优势 .

预编译头文件如何加快构建速度?对于给定的项目,大多数源文件共享一组公共头文件(尤其是为Windows构建的软件)。  许多头文件不经常更改。预编译头允许编译器将编译一组头的结果保存到PCH文件中,该文件可以在后续编译中代替这些头文件。如果你想了解更多, 本文讨论了预编译头的好处以及如何在项目中使用它们 .

预编译的头文件非常适合作为“设置它然后忘记它” 功能。  例如,在升级编译器时,它们很少需要注意。  然而,由于它们的性质,很少有情况会出错,而且很难找出原因。这篇文章将帮助您了解一些客户在使用VisualC++编译器的预编译头时遇到的一些问题。

概述

在MSVC编译器中创建或使用PCH文件时,您可能会看到这些错误代码和消息的间歇性生成失败:

  • 致命错误 C3859型 :超过PCH的虚拟内存范围;请使用’-ZmXXX’或更大的命令行选项重新编译
  • 致命错误 C1076型 :编译器限制:已达到内部堆;使用/Zm指定更高的限制
  • 致命错误 C1083型 :无法打开包含文件:“xyzzy”:没有这样的文件或目录

编译器使用这些诊断失败的原因有多种。所有这些失败都是虚拟内存空间中某种内存压力的结果,当编译器试图在特定的虚拟内存地址为PCH文件保留和分配空间时,就会出现这种压力。

如果你遇到PCH文件错误,你可以做的最好的事情之一就是移动到一个更新的Visual C++编译器。我们在VS 2015和VS 2017中修复了许多PCH内存压力错误。 Visual Studio 2017包含VS 2015.3中的编译器工具集以及VS 2017中的工具集,因此它是到Visual Studio 2017的简单迁移路径 . 2017版本15.3中附带的编译器提供了改进的诊断功能,帮助您了解遇到这些间歇性故障时发生的情况。

即使使用最新的编译器,当开发人员着手构建具有大量物理内核的机器时,他们仍然会在使用PCH文件时偶尔遇到从操作系统提交内存的失败。随着PCH文件的增长,优化构建的健壮性和构建速度非常重要。使用64位托管编译器可以提供帮助,还可以使用 /MP 编译器开关和MSBuild /maxcpucount: 开关。

影响PCH内存问题的区域

与PCH使用相关的生成失败通常有以下原因之一:

  • 在CL.EXE能够将其加载到内存之前,PCH所需的虚拟内存地址范围的碎片。
  • Windows操作系统在重载情况下无法在某个时间阈值内增加页面文件大小。

无法自动增加页面文件大小

一些使用许多核心(32+)计算机的开发人员报告说,在大量CL.EXE进程处于活动状态的高度并行构建期间,会看到上述间歇性错误消息。在使用 /maxcpucount ( /m )选项将MSBUILD.EXE与 /MP CL.EXE的选项。同时使用这两个选项,可以使同时运行的CL.EXE进程数成倍增加。

潜在的问题是Windows正在研究的一个潜在的文件系统瓶颈。在某些极端资源争用的情况下,操作系统将无法增加虚拟内存页面文件大小,即使有足够的磁盘空间这样做。这样的资源争用可以在一个高度并行的构建场景中实现,其中有许多CL.EXE进程同时运行。如果正在使用PCH,则每个CL.EXE进程将对 VirtualAlloc() ,要求它提交大块虚拟内存以加载PCH组件。如果系统页面文件被自动管理,操作系统可能会在服务所有页面文件之前超时 VirtualAlloc() 电话。如果在此场景中看到上述错误消息,手动管理页面文件设置可能会解决此问题。

手动管理Windows页面文件

下面是如何在Windows10上调整虚拟内存设置(这个过程与旧版本的Windows类似。)目标是设置页面文件设置,使它们足够大,可以处理所有并发的内存大小 VirtualAlloc() 试图加载PCH的每个CL.EXE进程所做的调用。通过将生成中最大的PCH文件的大小乘以生成期间任务管理器中观察到的CL.EXE进程的数量,可以进行信封背面的计算。请确保将初始大小设置为最大大小,这样Windows就不必调整页面文件的大小。

  1. 打开控制面板
  2. 选择系统和安全
  3. 选择系统
  4. 在“系统属性”对话框的“高级”选项卡中,选择“性能设置”按钮
  5. 选择“高级”选项卡上的虚拟内存“更改”按钮
  6. 关闭“自动管理所有驱动器的分页文件大小”并设置自定义大小。请注意,您应该将“初始大小”和“最大大小”设置为相同的值,并且应该将它们设置为足够大,以避免操作系统耗尽页面文件限制。

Windows settings for Virtual Memory

解决编译器体系结构、处理器和内存使用不平衡的问题

内存使用和预编译头的大多数问题来自于同时运行的多个CL.EXE进程中使用的大型PCH文件。这些建议将帮助您调整编译器体系结构和处理器使用情况,以便您可以根据所使用的PCH的大小使用适当的内存量。

更改编译器的主机体系结构

如果PCH文件很大(250 MB或更大),并且在使用x86托管编译器时收到上述内存不足错误消息,请考虑更改为x64托管编译器。x64托管编译器可以使用比x86托管编译器更多的(物理和虚拟)内存。您可以使用x64托管工具为任何体系结构生成应用程序。

要从命令行更改编译器的主机体系结构,只需运行相应的命令环境快捷方式(例如,“x64 Native Tools command Prompt”。)就可以通过键入 cl /Bv 在命令行上。

如果从命令行使用MSBuild,可以传递 /p:PreferredToolArchtecture=x64 到MSBuild . 如果您是在visualstudio中使用MSBuild进行构建,则可以编辑 .vcxproj 文件包含包含此属性的PropertyGroup。下面有关于如何在节下添加PropertyGroup的说明 “将MSBuild与64位编译器和工具一起使用” .

如果你用的是 /Zm 打开编译命令行,将其删除。在VisualStudio2015及以后版本中,不再需要该标志来容纳大型PCH文件。

更改编译中使用的处理器数

/MP 编译器选项 使用时,编译器将使用多个进程进行构建。每个进程将编译一个源文件(或“翻译单元”),并将其各自的PCH文件和编译器dll加载到该进程保留的虚拟内存空间中。在具有多个内核的机器上,这会很快导致操作系统耗尽物理内存。例如,在具有大PCH文件(例如250 MB)的64核计算机上,消耗的物理内存(而不是虚拟内存)很容易超过16 GB。当物理内存耗尽时,操作系统必须开始将进程内存交换到页文件,页文件(如果自动管理)可能需要增长以处理请求。当同时“增长”请求的数量达到临界点时,文件系统将在某个阈值内使其无法服务的所有请求失败。

一般的建议是,在跨进程并行编译时,不应超过物理内核的数量。尽管您可以通过超额订阅来获得更好的性能,但是您应该意识到这些内存错误的可能性,如果在生成过程中看到上述错误,请拨回所使用的并行量。

的默认设置 /MP 等于计算机上的物理核心数,但可以通过将其设置为较低的数字来限制它。例如,如果您的构建在64核机器上的两个工作进程中并行化,您可能需要设置 /MP32 为每个工作进程使用32个内核。请注意,MSBuild /maxcpucount (或 /m )设置引用MSBuild进程数。它的值有效地乘以编译器的 /MP 开关。如果你有 /maxcpucount:32 /MP 在32核机器上,默认值为32,最多可以同时运行1024个编译器实例。

限制并发编译器进程的数量可能有助于解决上述间歇性致命错误。

缩小PCH的尺寸

PCH文件越大,在构建期间运行的编译器的每个实例中消耗的内存就越多。PCH文件通常包含许多甚至没有被引用的头文件。当您升级到一个新的编译器工具集时,您可能还会发现您的PCH文件会增长。随着库头的大小在不同版本之间不断增大,包含它们的PCH文件也随之增大。

请注意,虽然理论上可以将PCH文件的大小最大为2GB,但任何超过250MB的PCH都应视为大型文件,因此更有可能包含未使用的头文件,并妨碍扩展到大型生成计算机。

“的使用” #pragma hdrstop PCH文件名 需要编译器处理输入文件直到hdrstop的位置,这会导致在加载PCH之前出现少量内存碎片。如果PCH的一个组件所需的地址范围当时仍在使用中,这可能导致PCH无法加载。建议通过命令行选项命名PCH文件 /Fp PCH文件名 这有助于编译器在进程执行的早期保留内存。

忽略 /Zm 旗帜

在VS2015之前,PCH由单个连续虚拟地址范围组成。如果PCH增长超过默认大小,则 /Zm 必须使用标志来启用更大的最大大小。在VS2015中,通过允许PCH包含多个地址范围,消除了此限制。这个 /Zm 旗帜被保留了下来 #pragma hdrstop 可能仅适用于包含单个连续地址范围的PCH的场景。这个 /Zm 标志不应用于任何其他场景,并且致命错误报告的值 C3859型 应该忽略(我们正在改进此错误消息,请参见下文。)

未来改进

我们正在努力使PCH文件在资源争用失败时更健壮,并使发出的错误更具可操作性。在Visual Studio 2017版本15.3中,编译器会发出详细消息,为编译器错误提供更多上下文 C3859型 . 例如,下面是这样一个失败的例子:

C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-ZmXX' or greaternote: PCH: Unable to get the requested block of memorynote: System returned code 1455 (ERROR_COMMITMENT_LIMIT): The paging file is too small for this operation to complete.note: please visit https://aka.ms/pch-help for more details

最后

感谢数百位提供反馈并帮助我们改进VisualStudio中C++体验的人。这篇博文中涉及的大多数问题和建议都来自我们的对话,因为您联系了我们的团队。

如果您对我们有任何反馈或建议,请告知我们。我们可以通过以下评论和电子邮件联系到您( visualcpp@microsoft.com )你可以通过 帮助>报告产品中的问题 ,或通过 开发者社区 . 你也可以在Twitter上找到我们( @视觉 )还有Facebook( msftvisualcpp软件 ).

© 版权声明
THE END
喜欢就支持一下吧,技术咨询可以联系QQ407933975
点赞0 分享