{"id":25898,"date":"2020-04-22T14:36:10","date_gmt":"2020-04-22T14:36:10","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cppblog\/?p=25898"},"modified":"2020-05-25T02:15:00","modified_gmt":"2020-05-25T02:15:00","slug":"finding-build-bottlenecks-with-cpp-build-insights","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/finding-build-bottlenecks-with-cpp-build-insights\/","title":{"rendered":"Finding build bottlenecks with C++ Build Insights"},"content":{"rendered":"<p>C++ Build Insights offers more than one way to investigate your C++ build times. In this article, we discuss two methods that you can use to identify bottlenecks in your builds: manually by using the <a href=\"https:\/\/devblogs.microsoft.com\/cppblog\/introducing-c-build-insights\/\">vcperf analysis tool<\/a>, or programmatically with the <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build-insights\/reference\/sdk\/overview?view=vs-2019\">C++ Build Insights SDK<\/a>. We present a case study that shows how to use these tools to speed up the Git for Windows open source project. We hope these tutorials will come in handy when analyzing your own builds.<\/p>\n<p><span style=\"font-size: 18pt;\"><strong>How to obtain and use vcperf<\/strong><\/span><\/p>\n<p>The examples in this article make use of <em>vcperf<\/em>, a tool that allows you to capture a trace of your build and to view it in the Windows Performance Analyzer (WPA). The latest version is available in Visual Studio 2019.<\/p>\n<p><span style=\"font-size: 14pt;\"><strong>1. Follow these steps to obtain and configure <em>vcperf <\/em>and WPA:<\/strong><\/span><\/p>\n<ol>\n<li>Download and install the latest <a href=\"https:\/\/visualstudio.microsoft.com\/downloads\/\">Visual Studio 2019<\/a>.<\/li>\n<li>Obtain WPA by downloading and installing the <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows-hardware\/get-started\/adk-install\">latest Windows ADK<\/a>.<\/li>\n<li>Copy the <em>perf_msvcbuildinsights.dll<\/em> file from your Visual Studio 2019\u2019s MSVC installation directory to your newly installed WPA directory. This file is the C++ Build Insights WPA add-in, which must be available to WPA for correctly displaying the C++ Build Insights events.\n<ol style=\"list-style-type: lower-alpha;\">\n<li>MSVC\u2019s installation directory is typically: <code>C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\<span class=\"pln\">{<\/span><span class=\"typ\">Edition<\/span><span class=\"pun\">}<\/span>\\VC\\Tools\\MSVC\\{Version}\\bin\\Hostx64\\x64<\/code>.<\/li>\n<li>WPA\u2019s installation directory is typically: <code>C:\\Program Files (x86)\\Windows Kits\\10\\Windows Performance Toolkit<\/code>.<\/li>\n<\/ol>\n<\/li>\n<li>Open the <em>perfcore.ini<\/em> file in your WPA installation directory and add an entry for the <em>perf_msvcbuildinsights.dll<\/em> file. This tells WPA to load the C++ Build Insights add-in on startup.<\/li>\n<\/ol>\n<p>You can also obtain the latest <em>vcperf<\/em> and WPA add-in by cloning and building the <a href=\"https:\/\/github.com\/microsoft\/vcperf\">vcperf GitHub repository<\/a>. Feel free to use your built copy in conjunction with Visual Studio 2019!<\/p>\n<p><span style=\"font-size: 14pt;\"><strong>2. Follow these steps to collect a trace of your build:<\/strong><\/span><\/p>\n<ol>\n<li>Open an elevated <strong><em>x64 Native Tools Command Prompt for VS 2019<\/em><\/strong>.<\/li>\n<li>Obtain a trace of your build:\n<ol style=\"list-style-type: lower-alpha;\">\n<li>Run the following command: <code>vcperf \/start MySessionName<\/code>.<\/li>\n<li>Build your C++ project from anywhere, even from within Visual Studio (<em>vcperf<\/em> collects events system-wide).<\/li>\n<li>Run the following command: <code>vcperf \/stop MySessionName outputFile.etl<\/code>. This command will stop the trace, analyze all events, and save everything in the <em>outputFile.etl<\/em> trace file.<\/li>\n<\/ol>\n<\/li>\n<li>Open the trace you just collected in WPA.<\/li>\n<\/ol>\n<p><span style=\"font-size: 18pt;\"><strong>Using the Build Explorer view in WPA<\/strong><\/span><\/p>\n<p>The first thing you\u2019ll want to do when first opening your trace in WPA is to open the <strong><em>Build Explorer<\/em><\/strong> view. You can do so by dragging it from the <strong><em>Graph Explorer<\/em><\/strong> pane to the <strong><em>Analysis<\/em><\/strong> window, as shown below.<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-25936 alignnone\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/04\/DraggingBuildExplorer-1.gif\" alt=\"Image DraggingBuildExplorer\" width=\"837\" height=\"649\" \/><\/p>\n<p>The Build Explorer view offers 4 presets that you can select from when navigating your build trace:<\/p>\n<ol>\n<li>Timelines<\/li>\n<li>Invocations<\/li>\n<li>Invocation Properties<\/li>\n<li>Activity Statistics<\/li>\n<\/ol>\n<p>Click on the drop-down menu at the top of the view to select the one you need. This step is illustrated below.<\/p>\n<p><img decoding=\"async\" width=\"477\" height=\"217\" class=\"wp-image-25901\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/04\/word-image-25.gif\" \/><\/p>\n<p>In the next 4 sections, we cover each of these presets in turn.<\/p>\n<p><span style=\"font-size: 18pt;\"><strong>Preset #1: Timelines<\/strong><\/span><\/p>\n<p>The <strong><em>Timelines <\/em><\/strong>preset shows how parallel invocations are laid out in time over the course of your build. Each timeline represents a virtual thread on which work happens. An invocation that does work on multiple threads will occupy multiple timelines.<\/p>\n<p><strong>N.B. <\/strong>Accurate parallelism for code generation is only available starting with Visual Studio 2019 version 16.4. In earlier versions, all code generation for a given compiler or linker invocation is placed on one timeline.<\/p>\n<p>When viewing the <strong><em>Timelines<\/em><\/strong> preset, hover over a colored bar to see which invocation it corresponds to. The following image shows what happens when hovering over a bar on the 5<sup>th<\/sup> timeline.<\/p>\n<p><img decoding=\"async\" width=\"749\" height=\"493\" class=\"wp-image-25902\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/04\/word-image-26.gif\" \/><\/p>\n<p><span style=\"font-size: 18pt;\"><strong>Preset #2: Invocations<\/strong><\/span><\/p>\n<p>The <strong><em>Invocations <\/em><\/strong>preset shows each invocation on its own timeline, regardless of parallelism. It gives a more detailed look into what\u2019s happening within the invocations. With this preset, hovering over a colored bar displays the activity being worked on by an invocation at any point in time. In the example below, we can see that the green bar in <strong><em>Linker 58<\/em> <\/strong>corresponds to the whole program analysis activity, a phase of link time code generation. We can also see that the output for <strong><em>Linker 58 <\/em><\/strong>was <strong><em>c2.dll<\/em><\/strong>.<\/p>\n<p><img decoding=\"async\" width=\"749\" height=\"493\" class=\"wp-image-25903 alignnone\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/04\/word-image-27.gif\" \/><\/p>\n<p><span style=\"font-size: 18pt;\"><strong>Preset #3: Invocation Properties<\/strong><\/span><\/p>\n<p>The <strong><em>Invocation Properties<\/em><\/strong> preset shows various properties for each invocation in the table at the bottom of the view. Find the invocation you are interested in to see miscellaneous facts about it such as:<\/p>\n<ul>\n<li>The version of CL or Link that was invoked.<\/li>\n<li>The working directory.<\/li>\n<li>Key environment variables such as PATH, or _CL_.<\/li>\n<li>The full command line, including arguments coming from response (.RSP) files or environment variables.<\/li>\n<\/ul>\n<p><strong>N.B. <\/strong>Command line or environment variables are sometimes shown in multiple entries if they are too long.<\/p>\n<p><img decoding=\"async\" width=\"749\" height=\"493\" class=\"wp-image-25904\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/04\/word-image-28.gif\" \/><\/p>\n<p><span style=\"font-size: 18pt;\"><strong>Preset #4: Activity Statistics<\/strong><\/span><\/p>\n<p>The <strong><em>Activity Statistics<\/em><\/strong> preset shows aggregated statistics for all build activities tracked by the <strong><em>Build Explorer<\/em><\/strong> view. Use it to learn, for example, the total duration of all linker and compiler invocations, or if your build times are dominated by parsing or code generation. Under this preset, the graph section of the view shows when each activity was active, while the table section shows the aggregated duration totals. Drill down on an activity to see all instances of this activity. The graph, table, and drill-down visuals are show in the sequence of images below. View the official <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build-insights\/reference\/sdk\/event-table?view=vs-2019\">C++ Build Insights event table<\/a> for a description of each activity.<\/p>\n<p><img decoding=\"async\" width=\"749\" height=\"377\" class=\"wp-image-25905\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/04\/word-image-29.gif\" \/><\/p>\n<p><img decoding=\"async\" width=\"749\" height=\"368\" class=\"wp-image-25906\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/04\/word-image-30.gif\" \/><\/p>\n<p><img decoding=\"async\" width=\"749\" height=\"387\" class=\"wp-image-25907\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/04\/word-image-31.gif\" \/><\/p>\n<p><span style=\"font-size: 18pt;\"><strong>Putting it all together: a bottleneck case study<\/strong><\/span><\/p>\n<p>In this case study, we use a real open source project from GitHub and show you how we found and fixed a bottleneck.<\/p>\n<p>Use these steps if you would like to follow along:<\/p>\n<ol>\n<li>Clone the <a href=\"https:\/\/github.com\/git-for-windows\/git\">Git for Windows GitHub repository<\/a>.<\/li>\n<li>Switch to the <strong><em>vs\/master<\/em><\/strong> branch.<\/li>\n<li>Open the git\\git.sln solution file, starting from the root of the repository.<\/li>\n<li>Build the <strong><em>x64 Release<\/em> <\/strong>configuration. This will pull all the package dependencies and do a full build.<\/li>\n<li>Obtain a trace for a full rebuild of the solution:\n<ol>\n<li>Open an elevated command prompt with <strong><em>vcperf<\/em><\/strong> on the PATH.<\/li>\n<li>Run the following command: <code>vcperf \/start Git<\/code><\/li>\n<li>Rebuild the <strong><em>x64 Release<\/em><\/strong> configuration of the git\\git.sln solution file in Visual Studio.<\/li>\n<li>Run the following command: <code>vcperf \/stop Git git.etl<\/code>. This will save a trace of the build in git.etl.<\/li>\n<\/ol>\n<\/li>\n<li>Open the trace in WPA.<\/li>\n<\/ol>\n<p>We use the <strong><em>Timelines<\/em><\/strong> preset of the Build Explorer view, and immediately notice a long-running invocation that seems to be a bottleneck at the beginning of the build.<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-25934\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/04\/GitTimelineHover.gif\" alt=\"Image GitTimelineHover\" width=\"837\" height=\"649\" \/><\/p>\n<p>We switch over to the <strong><em>Invocations<\/em><\/strong> preset to drill down on that particular invocation. We notice that all files are compiled sequentially. This can be seen by the small teal-colored bars appearing one after the other on the timeline, instead of being stacked one on top of the other.<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-25932\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/04\/GitInvestigateInvocations.gif\" alt=\"Image GitInvestigateInvocations\" width=\"837\" height=\"649\" \/><\/p>\n<p>We look at the <strong><em>Invocation Properties <\/em><\/strong>for this invocation, and notice that the command line does not have \/MP, the flag that enables parallelism in CL invocations. We also notice from the <strong><em>WorkingDirectory<\/em><\/strong> property that the project being built is called <strong><em>libgit<\/em><\/strong>.<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-25933\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/04\/GitInvestigateProperties.gif\" alt=\"Image GitInvestigateProperties\" width=\"837\" height=\"649\" \/><\/p>\n<p>We enable the \/MP flag in the properties page for the <strong><em>libgit<\/em><\/strong> projet in Visual Studio.<\/p>\n<p><img decoding=\"async\" width=\"817\" height=\"544\" class=\"wp-image-25911\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/04\/word-image-35.gif\" \/><\/p>\n<p>We capture another full build trace using the steps at the beginning of this section to confirm that we mitigated the issue. The build time was reduced from around 120 seconds to 80 seconds, a 33% improvement.<\/p>\n<p><img decoding=\"async\" width=\"837\" height=\"649\" class=\"wp-image-25912\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/04\/word-image-36.gif\" \/><\/p>\n<p><span style=\"font-size: 18pt;\"><strong>Identifying bottlenecks using the C++ Build Insights SDK<\/strong><\/span><\/p>\n<p>Most analysis tasks performed manually with <strong><em>vcperf<\/em><\/strong> and WPA can also be performed programmatically using the C++ Build Insights SDK. To illustrate this point, we\u2019ve prepared the <strong><em>BottleneckCompileFinder<\/em><\/strong> SDK sample. It emits a warning when it finds a bottleneck compiler invocation that doesn\u2019t use the \/MP switch. An invocation is considered a bottleneck if no other compiler or linker invocation is ever invoked alongside it.<\/p>\n<p>Let\u2019s repeat the <strong><em>Git for Windows<\/em><\/strong> case study from the previous section, but this time by using the <strong><em>BottleneckCompileFinder <\/em><\/strong>to see what it finds<em>. <\/em>Use these steps if you want to follow along:<\/p>\n<ol>\n<li>Clone the <a href=\"https:\/\/github.com\/microsoft\/cpp-build-insights-samples\">C++ Build Insights SDK samples GitHub repository<\/a> on your machine.<\/li>\n<li>Build the <strong><em>Samples.sln <\/em><\/strong>solution, targeting the desired architecture (x86 or x64), and using the desired configuration (debug or release). The sample\u2019s executable will be placed in the <code>out\/{architecture}\/{configuration}\/BottleneckCompileFinder<\/code> folder, starting from the root of the repository.<\/li>\n<li>Follow the steps from the <strong><em>Putting it all together: a bottleneck case study<\/em><\/strong> section to collect a trace of the <strong><em>Git for Windows<\/em><\/strong> solution. Use the \/stopnoanalyze command instead of the \/stop command when stopping your trace.<\/li>\n<li>Pass the collected trace as the first argument to the <strong><em>BottleneckCompileFinder<\/em><\/strong> executable.<\/li>\n<\/ol>\n<p>As shown below, <strong><em>BottleneckCompileFinder<\/em><\/strong> correctly identifies the <strong><em>libgit<\/em><\/strong> project and emits a warning. It also identifies one more: <strong><em>xdiff<\/em><\/strong>, though this one has a small duration and doesn\u2019t need to be acted upon.<\/p>\n<p><img decoding=\"async\" width=\"651\" height=\"379\" class=\"wp-image-25913\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/04\/word-image-37.gif\" \/><\/p>\n<p><span style=\"font-size: 18pt;\"><strong>Going over the sample code<\/strong><\/span><\/p>\n<p>We first filter all start activity, stop activity, and simple events by asking the C++ Build Insights SDK to forward what we need to the OnStartInvocation, OnStopInvocation, and OnCompilerCommandLine functions. The name of the functions has no effect on how the C++ Build Insights SDK will filter the events; only their parameters matter.<\/p>\n<pre class=\"prettyprint\">AnalysisControl OnStartActivity(const EventStack&amp; eventStack)\r\n    override\r\n{\r\n    MatchEventStackInMemberFunction(eventStack, this,\r\n        &amp;BottleneckCompileFinder::OnStartInvocation);\r\n\r\n    return AnalysisControl::CONTINUE;\r\n}\r\n\r\nAnalysisControl OnStopActivity(const EventStack&amp; eventStack)\r\n    override\r\n{\r\n    MatchEventStackInMemberFunction(eventStack, this,\r\n        &amp;BottleneckCompileFinder::OnStopInvocation);\r\n\r\n    return AnalysisControl::CONTINUE;\r\n}\r\n\r\nAnalysisControl OnSimpleEvent(const EventStack&amp; eventStack)\r\n    override\r\n{\r\n    MatchEventStackInMemberFunction(eventStack, this,\r\n        &amp;BottleneckCompileFinder::OnCompilerCommandLine);\r\n\r\n    return AnalysisControl::CONTINUE;\r\n}<\/pre>\n<p>Our OnCompilerCommandLine function keeps track of all compiler invocations that don\u2019t use the \/MP flag. This information will be used later to emit a warning about these invocations if they are a bottleneck.<\/p>\n<pre class=\"prettyprint\">void OnCompilerCommandLine(Compiler cl, CommandLine commandLine)\r\n{\r\n    auto it = concurrentInvocations_.find(cl.EventInstanceId());\r\n\r\n    if (it == concurrentInvocations_.end()) {\r\n        return;\r\n    }\r\n\r\n    \/\/ Keep track of CL invocations that don't use MP so that we can\r\n    \/\/ warn the user if this invocation is a bottleneck.\r\n\r\n    std::wstring str = commandLine.Value();\r\n\r\n    if (str.find(L\" \/MP \") != std::wstring::npos ||\r\n        str.find(L\" -MP \") != std::wstring::npos)\r\n    {\r\n        it-&gt;second.UsesParallelFlag = true;\r\n    }\r\n}<\/pre>\n<p>Our OnStartInvocation and OnStopInvocation functions keep track of concurrently running invocations by adding them in a hash map on start, and by removing them on stop. As soon as 2 invocations are active at the same time, we consider all others to no longer be bottlenecks. If a compiler invocation is marked a bottleneck once we reach its stop event, it means there never was another invocation that started while it was running. We warn the user if these invocations do not make use of the \/MP flag.<\/p>\n<pre class=\"prettyprint\">void OnStartInvocation(InvocationGroup group)\r\n{\r\n    \/\/ We need to match groups because CL can\r\n    \/\/ start a linker, and a linker can restart\r\n    \/\/ itself. When this happens, the event stack\r\n    \/\/ contains the parent invocations in earlier\r\n    \/\/ positions.\r\n\r\n    \/\/ A linker that is spawned by a previous tool is \r\n    \/\/ not considered an invocation that runs in\r\n    \/\/ parallel with the tool that spawned it.\r\n    if (group.Size() &gt; 1) {\r\n        return;\r\n    }\r\n\r\n    \/\/ An invocation is speculatively considered a bottleneck \r\n    \/\/ if no other invocations are currently running when it starts.\r\n    bool isBottleneck = concurrentInvocations_.empty();\r\n\r\n    \/\/ If there is already an invocation running, it is no longer\r\n    \/\/ considered a bottleneck because we are spawning another one\r\n    \/\/ that will run alongside it. Clear its bottleneck flag.\r\n    if (concurrentInvocations_.size() == 1) {\r\n        concurrentInvocations_.begin()-&gt;second.IsBottleneck = false;\r\n    }\r\n\r\n    InvocationInfo&amp; info = concurrentInvocations_[\r\n        group.Back().EventInstanceId()];\r\n\r\n    info.IsBottleneck = isBottleneck;\r\n}\r\n\r\nvoid OnStopInvocation(Invocation invocation)\r\n{\r\n    using namespace std::chrono;\r\n\r\n    auto it = concurrentInvocations_.find(invocation.EventInstanceId());\r\n\r\n    if (it == concurrentInvocations_.end()) {\r\n        return;\r\n    }\r\n\r\n    if (invocation.Type() == Invocation::Type::CL &amp;&amp;\r\n        it-&gt;second.IsBottleneck &amp;&amp;\r\n        !it-&gt;second.UsesParallelFlag)\r\n    {\r\n        std::cout &lt;&lt; std::endl &lt;&lt; \"WARNING: Found a compiler invocation that is a \" &lt;&lt;\r\n            \"bottleneck but that doesn't use the \/MP flag. Consider adding \" &lt;&lt;\r\n            \"the \/MP flag.\" &lt;&lt; std::endl;\r\n\r\n        std::cout &lt;&lt; \"Information about the invocation:\" &lt;&lt; std::endl;\r\n        std::wcout &lt;&lt; \"Working directory: \" &lt;&lt; invocation.WorkingDirectory() &lt;&lt; std::endl;\r\n        std::cout &lt;&lt; \"Duration: \" &lt;&lt; duration_cast&lt;seconds&gt;(invocation.Duration()).count() &lt;&lt;\r\n            \" s\" &lt;&lt; std::endl;\r\n    }\r\n\r\n    concurrentInvocations_.erase(invocation.EventInstanceId());\r\n}<\/pre>\n<p><span style=\"font-size: 18pt;\"><strong>Tell us what you think!<\/strong><\/span><\/p>\n<p>We hope the information in this article has helped you understand how you can use the <strong><em>Build Explorer<\/em><\/strong> view from <strong><em>vcperf<\/em><\/strong> and WPA to diagnose bottlenecks in your builds. We also hope that the provided SDK sample helped you build a mental map of how you can translate manual analyses into automated ones.<\/p>\n<p>Give <strong><em>vcperf<\/em><\/strong> a try today by downloading the latest version of <a href=\"https:\/\/visualstudio.microsoft.com\/downloads\/\">Visual Studio 2019<\/a>, or by cloning the tool directly from the <a href=\"https:\/\/github.com\/microsoft\/vcperf\">vcperf Github repository<\/a>. Try out the <strong><em>BottleneckCompileFinder<\/em><\/strong> sample from this article by cloning the <a href=\"https:\/\/github.com\/microsoft\/cpp-build-insights-samples\">C++ Build Insights samples repository<\/a> from GitHub, or refer to the official <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build-insights\/reference\/sdk\/overview?view=vs-2019\">C++ Build Insights SDK documentation<\/a> to build your own analysis tools.<\/p>\n<p>Have you found bottlenecks in your builds using <strong><em>vcperf<\/em><\/strong> or the C++ Build Insights SDK? Let us know in the comments below, on Twitter <a href=\"https:\/\/twitter.com\/visualc\">(@VisualC<\/a>), or via email at <a href=\"mailto:visualcpp@microsoft.com\">visualcpp@microsoft.com<\/a>.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>C++ Build Insights offers more than one way to investigate your C++ build times. In this article, we discuss two methods that you can use to identify bottlenecks in your builds: manually by using the vcperf analysis tool, or programmatically with the C++ Build Insights SDK. We present a case study that shows how to [&hellip;]<\/p>\n","protected":false},"author":6966,"featured_media":25198,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1,239],"tags":[],"class_list":["post-25898","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cplusplus","category-diagnostics"],"acf":[],"blog_post_summary":"<p>C++ Build Insights offers more than one way to investigate your C++ build times. In this article, we discuss two methods that you can use to identify bottlenecks in your builds: manually by using the vcperf analysis tool, or programmatically with the C++ Build Insights SDK. We present a case study that shows how to [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/25898","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/users\/6966"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=25898"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/25898\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/25198"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=25898"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=25898"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=25898"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}