58

Visual Studio 2017 性能提升和建议 – C++

 6 years ago
source link: https://blogs.msdn.microsoft.com/c/2018/01/26/visual-studio-2017-%E6%80%A7%E8%83%BD%E6%8F%90%E5%8D%87%E5%92%8C%E5%BB%BA%E8%AE%AE/?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
[原文发表地址] Visual Studio 2017 Throughput Improvements and Advice [原文发表时间] 2018/01/4 随着C++项目的壮大和优化器的日渐复杂,编译器的编译时间,或者说是性能,逐渐成为人们关注的焦点。这是我们Visual C++组非常关注的问题,也成为了15.5版本和未来工作的重点。我想花几分钟时间来为各位总结一下我们最近为提升性能所做的特殊的更改,并且可以为你提升编译工程的性能提几点建议。 这里需要注意的是,并非所有的更改都可以提升所有场景的性能。把编译时间降低到一个期望值是一个长远的事业。最近我们开始把AAA游戏作为一个基准。未来还需要付出更多的努力。 VS工具集有三个部分需要分别改进。第一就是编译器前端,也就是c1xx.dll的执行。这是一个将cpp文件作为输入并且生成一种不依赖于中间语言的语言的工具,或者IL,即将被输入到编译器后端的内容。编译器后端也就是c2.dll。它从前端读取IL并从中生成包含真正机器代码的obj。最后是链接器,它将读取后端编译器生成的各种obj和lib,并且将他们合并生成一个最终的二进制文件。 编译器前端性能 在许多工程里,前端编译时间阻碍了整体性能的提升。幸运的是,通过直接给msbuild或者其他编译系统加/MP选项(该选项可以使cl.exe同时处理多个文件),或者甚至可以使用像incredibuild这样的工具通过分布式机器来加速。想要提升性能的第一步是在编译工程时进行高效的分布和并行。 第二步是确保你高效使用了PCH文件。一个PCH文件基本上是cl.exe 充分解析了.h文件之后的内存转储-解决了每次都需要这样做的麻烦。你会被它的作用所震惊,头文件(比如windows.h或者一些DirectX头)一旦被完全预处理会变得非常大,并且常常会成为一个后处理源文件的最主要的部分。PCH文件会开启一个新世界。这里的主要操作是只将会被频繁更改的文件包含进来,这就保证PCH会帮你改进很多性能。 最后一点建议是对#include的使用限制。在PCH文件之外,包含一个文件是一个非常昂贵的操作,这就牵扯到了在包含的路径里搜索每一个文件夹的问题。很多文件的输入输出操作,这是一个每次都要被重复的传递性操作。这就是PCH会起很大作用的原因。在微软内部,人们有很多有关“只包含你要使用的文件”的成功案例。/showInclude选项可以让你认识到包含文件是多么的昂贵,并且可以指导你只包含你需要用的东西。 最后,我想让你了解一下/Bt选项。这个选项可以显示每个文件的前端(同样也包括后端和链接时间)编译时间。它可以帮你查清性能低的原因,让你知道哪个文件需要你花时间去优化。 以下是我们为提升前端性能所做的更改。 刷新PGO计数 PGO,或者叫配置文件优化,是一种编译器后端科技,在微软被广泛使用。基本原理是你生成了一个特殊检测版本的产品,通过运行测试用例生成配置文件,基于已收集的数据进行重新编译/优化。 我们发现在编译或者优化前端二进制文件(c1xx.dll)时使用的是旧的配置文件数据。当我们重新检测或者重新收集 PGO数据时,会看到10%的性能提升。 这里我们学到的是,如果在产品中用PGO提升性能,请确保定期收集训练过的数据。 移除_assume的使用 _assume(0)给编译器后端传递一个信号, 告诉它一个特定的代码路径(也许是一个默认情况的标签,等等)无法到达。许多产品会把它包含在一个宏里,并命名为类似于UNREACHABLE这样的名字,然后执行,这样debug版本会声明这个信号,ship版本会把这个信号传递给编译器。编译器会做一些操作比如移除枝干或者转移目标声明。 这是有道理的,如果在运行时一个_assume(0)声明实际上是可到达的,结果就是生成错误代码。这会在很多不同的情况下带来很多问题(并且一些人抱怨这会引发安全问题)-- 所以我们做了一个实验来看看通过重新定义一个宏来简单的移除所有的_assume(0)会带来的影响。如果回归很小,也许不值得把它放在产品中免得引发其他的问题。 令我们惊讶的是,移除_assume声明使前端性能提升了1%-2%。这就很容易做决定了。这一现象的根源就是尽管在很多情况下_assume对应优化器是一个有效的信号,但实际上它也许会阻碍其他优化(尤其是比较新的优化)。在未来的版本中我们将持续对_assume进行改进。 改进winmd文件加载 在winmd文件加载问题上我们做了很多更改,旨在提升10%的加载性能(这一项大概占总编译时间的1%)。这只会影响UWP工程。 编译器后端 编译器后端包含了优化器。这里有两个等级的性能问题,“常规”问题(在这里我们做了大量的工作希望能有1-2%的提升),和“长远”问题,这里有一个特殊的方法会导致一些优化到达了一个不合理的路径并且会花30s或者更长时间去编译 — 但是大部分人没被影响。我们关心这个并且一直为之改进。 如果你使用/Bt选项并且看到一个异常的文件花费了非正常的时间做后端编译,下一步就是在编译时使用/d2cgsummary选项。Cgsummary(或者叫代码生成概要)将会告诉你哪个函数花费的时间长。如果这个函数不在你的关键性能路径中,说明你很幸运,那么你就可以用以下的方法为该函数关闭优化: #pragma optimize("", off) void foo() { ... } #pragma optimize("", on) 那么这个函数就不会被优化。和我们保持联系,我们可以帮你看看是否能修复这个问题。 除了为编译时间不正常的方法关闭优化之外,我需要提示你,使用 _forceinline时要当心。通常客户会使用forceinline让内联器做他们想做的事情,这种情况下,我的建议是尽可能的有针对性的使用。编译器后端会非常非常重视_forceinline。它会免除所有的内联预算检查(_forceinline的花费不会对内联预算不利)。这些年我们看了许多案例,以代码质量为由随意使用_forceinline是性能提神的主要阻碍。基本的,不像其他编译器,我们经常通过前端的IL内联预优化的方法。这样做有时候有利,我们为不同的内容做不同的优化,但一个弊端是,很多工作我们将无法恢复。如果你有一个很深的内联树,那么这将很快变得无法控制。这就是碰到像是Tensorflow/libsodium这样的地方编译时间过长的根源。这是我们未来版本将要着眼改进的地方。 当使用LTCG build时请了解一下iLTCG。增量LTCG是一项新科技,使用它我们只需要对LTCG...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK