c++ - 使用 Visual Studio Compiler 分析内联 C++ 函数

标签 c++ visual-studio profiling inline visual-studio-debugging

当编译器内联了很多代码时,如何理解 Windows 上的 C++ 分析数据? IE。我当然想测量实际运行的代码,所以根据定义,我将测量代码的优化构建。但似乎我尝试的所有工具都没有真正设法解决内联函数。

我已经尝试了 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;
}

在 Release模式下使用 Visual Studio 编译它。或者在命令行上,尝试 cl /O2 /Zi /Zo /EHsc main.cpp .然后尝试使用 Visual Studio 中的 CPU Sampling Profiler 对其进行分析。你最多会看到这样的东西:

confusing profile since inline frames are missing

VTune 2018 在 Windows 上看起来很相似。在 Linux 上,perf 和 VTune 显示来自内联函数的帧没有问题......在我看来,这个功能对于 C++ 工具至关重要,真的不是非高级/终极 Visual Studio 工具链的一部分吗? Windows 上的人如何处理? /Zo有什么意义然后?

编辑:我只是尝试用 clang 编译上面的最小示例,它产生了不同但仍然不令人满意的结果?我编译了 clang 6.0.0(主干),从 LLVM rev 318844 和 clang rev 318874 构建。然后我用 clang++ -std=c++17 -O2 -g main.cpp -o main.exe 编译我的代码并再次在 Visual Studio 中使用 Sampling Profiler 运行生成的可执行文件,结果是:

inline frames are shown in profile after compiling with clang

所以现在我看到了 burn功能,但丢失了源文件信息。另外,uniform_real_distribution仍然没有在任何地方显示。

编辑 2:正如评论中所建议的,我现在也尝试了 clang-cl使用与 cl 相同的参数以上,即:clang-cl.exe /O2 /Zi /Zo /EHsc main.cpp .这产生与 clang.exe 相同的结果,但我们也得到了一些有效的源映射:

clang-cl shows inliners and somewhat functional source mapping

编辑 3:我原本以为 clang 会神奇地解决这个问题。没有,很遗憾。大多数内联框架仍然丢失:(

编辑 4:对于使用 MSVC/PDB 构建的应用程序构建,VTune 不支持内联框架:https://software.intel.com/en-us/forums/intel-vtune-amplifier-xe/topic/749363

最佳答案

I have tried both the sampling profiler in Visual Studio 2017 Professional as well as VTune 2018. I have tried to enable /Zo, but it does not seem to have any affect.

I have found the following resource which seems to indicate that only Visual Studio Ultimate or Premium support inline frame information - is this still true for Visual Studio 2017?



幸运的是,我已经安装了三个不同版本的 VS。我可以告诉您有关支持内联函数信息功能的更多信息,如 article 中所述。你引用了:
  • 即使我指定了/d2Zi+,VS Community 2013 Update 5 也不支持显示内联函数。似乎只有 VS 2013 Premium 或 Ultimate 支持它。
  • VS Community 2015 Update 3 确实支持显示内联函数(在 article 中讨论的功能)。默认情况下,指定/Zi。 /Zo is enabled implicitly with /Zi ,因此您不必明确指定它。因此,您不需要 VS 2015 Premium 或 Ultimate。
  • 具有最新更新的 VS Community 2017 不支持显示内联函数,而不管/Zi 和/Zo。似乎它仅在 VS 2017 Professional 和/或 Enterprise 中受支持。

  • VC++ 博客上没有关于 VS 2017 采样分析器的任何改进的公告,所以我认为与 VS Community 2015 的分析器相比,它没有任何更好的表现。

    请注意,不同版本的编译器可能会做出不同的优化决策。例如,我观察到 VS 2013 和 2015 没有内联 burn功能。

    通过使用 VS Community 2015 Update 3,我得到的分析结果与 third picture 中显示的非常相似。并突出显示相同的代码。

    现在我将讨论这些附加信息在解释分析结果时如何有用,如何通过更多的努力手动获得它,以及如何解释结果,尽管有内联函数。

    How can I make sense of C++ profiling data on Windows, when a lot of code gets inlined by the compiler?



    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;
    

    如果没有该功能,您将必须手动反汇编发出的可执行二进制文件(使用 VS 调试器或使用 dumpbin )并找到所有 x86 call指示。通过将其与源代码中调用的函数进行比较,您可以确定哪些函数被内联。

    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;
    

    考虑到所有这些,我在这里看到的唯一潜在优化机会是记住 log2 的结果。 .

    VTune 采样分析器肯定比 VS 采样分析器更强大。特别是,VTune 将成本归因于单个源代码行或汇编指令。然而,这种归因是高度近似的,而且通常是荒谬的。因此,在解释以这种方式可视化的结果时,我会非常小心。我不确定 VTune 是否支持 Enhance Optimized Debugging信息或支持将成本归因于内联函数的程度。提出这些问题的最佳地点是 Intel VTune Amplifier community forum .

    关于c++ - 使用 Visual Studio Compiler 分析内联 C++ 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47541598/

    相关文章:

    c++ - 在分配字符串时分配右值引用

    c++ - 在 Visual Studio 中,如果组件已过时,如何强制构建?

    python - 如何逐行分析cython功能

    node.js - 在 Linux 上分析 Node.js Web 应用程序

    c++ - 如何修复学生记录计划的 GPA 输出

    c++ - 在 Windows 服务器域内检索用户的名字和姓氏,给定其登录名

    angular - 在 Angular 5 应用程序中使用 @ngx-translate/core 时出错

    css - 无法在 Visual Studio 2010 中设置背景图像

    Haskell,分析导入库的内存使用情况

    C++ 将成员变量传递给成员类