mil*_*anw 15 c++ profiling inline visual-studio visual-studio-debugging
当大量代码被编译器内联时,我怎样才能理解Windows上的C++分析数据?即我当然想要测量实际运行的代码,因此根据定义,我将测量代码的优化构建.但似乎我尝试实际设法解决内联函数的工具都没有.
我已经尝试过Visual Studio 2017 Professional和VTune 2018中的采样分析器.我试过启用/Zo,但它似乎没有任何影响.
我发现以下资源似乎表明只有Visual Studio Ultimate或Premium支持内联框架信息 - 这仍然适用于Visual Studio 2017吗?https://social.msdn.microsoft.com/Forums/en-US/9df15363-5aae-4f0b-a5ad-dd9939917d4c/which-functions-arent-pgo-optimized-using-profile-data?forum=vsdebug
这是一个示例代码:
#include <cmath>
#include <random>
#include <iostream>
inline double burn()
{
std::uniform_real_distribution<double> uniform(-1E5, 1E5);
std::default_random_engine engine;
double s = 0;
for (int i = 0; i < 100000000; ++i) {
s += uniform(engine);
}
return s;
}
int main()
{
std::cout << "random sum: " << burn() << '\n';
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在发布模式下使用Visual Studio编译它.或者在命令行上,尝试cl /O2 /Zi /Zo /EHsc main.cpp.然后尝试使用Visual Studio中的CPU Sampling Profiler对其进行配置.你最多会看到这样的事情:
VTune 2018在Windows上看起来很相似.在Linux上,perf和VTune显示来自内联函数的帧没有问题...这个功能,在我看来对C++工具至关重要,真的不是非Premium/Ultimate Visual Studio工具链的一部分吗?Windows上的人们如何处理这个问题?那有什么意义/Zo呢?
编辑:我只是尝试用clang编译上面的最小例子,它产生不同的,但仍然不满意的结果?我编译了clang 6.0.0(trunk),从LLVM rev 318844和clang rev 318874构建.然后我编译我的代码clang++ -std=c++17 -O2 -g main.cpp -o main.exe并再次使用Visual Studio中的Sampling Profiler运行生成的可执行文件,结果是:
所以现在我看到了这个burn功能,但丢失了源文件信息.此外,uniform_real_distribution仍然没有在任何地方显示.
编辑2:正如评论中所建议的那样,我现在也尝试了clang-cl与上述相同的论点cl,即:clang-cl.exe /O2 /Zi /Zo /EHsc main.cpp.这产生了相同的结果clang.exe,但我们也得到了一些有效的源映射:
编辑3:我原本以为clang会神奇地解决这个问题.遗憾的是,它没有.大多数内联框架仍然缺失:(
编辑4: VTune中不支持嵌入式框架,用于使用MSVC/PDB版本构建的应用程序:https://software.intel.com/en-us/forums/intel-vtune-amplifier-xe/topic/749363
我尝试过 Visual Studio 2017 Professional 和 VTune 2018 中的采样分析器。我尝试启用 /Zo,但它似乎没有任何影响。
我发现以下资源似乎表明只有 Visual Studio Ultimate 或 Premium 支持内联框架信息 - 对于 Visual Studio 2017 来说这仍然如此吗?
幸运的是,我已经安装了三个不同版本的VS。我可以告诉您有关内联函数信息功能支持的更多信息,如您引用的文章中所述:
VC++ 博客上没有关于 VS 2017 采样分析器的任何改进的公告,因此我认为它与 VS Community 2015 的分析器相比没有任何改进。
请注意,不同版本的编译器可能会做出不同的优化决策。例如,我观察到 VS 2013 和 2015 没有内联该burn函数。
通过使用 VS Community 2015 Update 3,我得到的分析结果与第三张图片中显示的结果非常相似,并且突出显示了相同的代码。
现在,我将讨论这些附加信息在解释分析结果时如何有用,如何通过更多努力手动获取该结果,以及如何解释结果(尽管有内联函数)。
当编译器内联大量代码时,如何理解 Windows 上的 C++ 分析数据?
VS 分析器只会将成本归因于未内联的函数。对于内联的函数,成本将被累加并包含在某些未内联的调用者函数(在本例中为函数burn)中。
burn通过将非内联调用函数的估计执行时间(如图所示)相加,我们得到 31.3 + 22.7 + 4.7 + 1.1 = 59.8%。另外,Function Body如图所示的预计执行时间为40.2%。请注意,59.8% + 40.2% = 100% 的时间花在 上burn,这是应该的。换句话说,40.2% 的时间burn花在函数体以及内联到其中的任何函数上。
40.2%很多了。下一个逻辑问题是,哪些函数被内联burn?通过使用我之前讨论的该功能(VS Community 2015 中提供),我可以确定以下函数已内联在 中burn:
std::mersenne_twister_engine<unsigned int,32,624,397,31,2567483615,11,4294967295,7,2636928640,15,4022730752,18,1812433253>::{ctor};
std::mersenne_twister<unsigned int,32,624,397,31,2567483615,11,7,2636928640,15,4022730752,18>::{ctor};
std::mersenne_twister<unsigned int,32,624,397,31,2567483615,11,7,2636928640,15,4022730752,18>::seed;
std::uniform_real<double>::operator();
std::uniform_real<double>::_Eval;
std::generate_canonical;
Run Code Online (Sandbox Code Playgroud)
如果没有该功能,您将必须手动反汇编发出的可执行二进制文件(使用 VS 调试器或使用dumpbin)并找到所有 x86call指令。通过将其与源代码中调用的函数进行比较,您可以确定哪些函数被内联。
VS 采样分析器的功能至此为止(包括 VS 2017)。但这确实不是一个重大限制。通常,由于编译器对每个函数的大小施加了硬性上限,因此不会在同一函数中内联许多函数。因此,通常可以手动检查每个内联函数的源代码和/或汇编代码,并查看该代码是否会显着影响执行时间。我这样做了,很可能的情况是主体burn(不包括内联函数)和这两个内联函数主要负责这 40.2%。
std::mersenne_twister<unsigned int,32,624,397,31,2567483615,11,7,2636928640,15,4022730752,18>::seed;
std::uniform_real<double>::_Eval;
Run Code Online (Sandbox Code Playgroud)
考虑到所有这些,我在这里看到的唯一潜在的优化机会是记住log2.
VTune 采样分析器肯定比 VS 采样分析器更强大。特别是,VTune 将成本归因于各个源代码行或汇编指令。然而,这种归因是高度近似的,而且常常是荒谬的。因此,在解释以这种方式可视化的结果时,我会非常小心。我不确定 VTune 是否支持增强优化调试信息,或者在多大程度上支持将成本归因于内联函数。提出这些问题的最佳地点是英特尔 VTune Amplifier 社区论坛。