Skip to content

Commit

Permalink
Merge pull request microsoft#7 from kevcadieux/function-bottleneck-sa…
Browse files Browse the repository at this point in the history
…mple

Function bottleneck sample
  • Loading branch information
kevcadieux authored Jul 21, 2020
2 parents b0e30b7 + 7955d9f commit edc69ad
Show file tree
Hide file tree
Showing 6 changed files with 392 additions and 0 deletions.
161 changes: 161 additions & 0 deletions FunctionBottlenecks/FunctionBottlenecks.vcxproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{F002C795-7D92-4146-8A7C-BBEB946F4309}</ProjectGuid>
<RootNamespace>FunctionBottlenecks</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>FunctionBottlenecks</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)out\$(Platform)\$(Configuration)\$(ProjectName)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)out\$(Platform)\$(Configuration)\$(ProjectName)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)out\$(Platform)\$(Configuration)\$(ProjectName)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)out\$(Platform)\$(Configuration)\$(ProjectName)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\packages\Microsoft.Cpp.BuildInsights.1.1.0\build\native\Microsoft.Cpp.BuildInsights.targets" Condition="Exists('..\packages\Microsoft.Cpp.BuildInsights.1.1.0\build\native\Microsoft.Cpp.BuildInsights.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Cpp.BuildInsights.1.1.0\build\native\Microsoft.Cpp.BuildInsights.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Cpp.BuildInsights.1.1.0\build\native\Microsoft.Cpp.BuildInsights.targets'))" />
</Target>
</Project>
25 changes: 25 additions & 0 deletions FunctionBottlenecks/FunctionBottlenecks.vcxproj.filters
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>
191 changes: 191 additions & 0 deletions FunctionBottlenecks/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#include <CppBuildInsights.hpp>

using namespace Microsoft::Cpp::BuildInsights;
using namespace Activities;
using namespace SimpleEvents;

class FunctionBottlenecks : public IAnalyzer
{
struct IdentifiedFunction
{
std::string Name;
std::chrono::milliseconds Duration;
double Percent;
unsigned ForceInlineeSize;

bool operator<(const IdentifiedFunction& other) const {
return Duration > other.Duration;
}
};

public:
FunctionBottlenecks():
pass_{0},
cachedInvocationDurations_{},
identifiedFunctions_{},
forceInlineSizeCache_{}
{}

AnalysisControl OnBeginAnalysisPass() override
{
++pass_;
return AnalysisControl::CONTINUE;
}

AnalysisControl OnStopActivity(const EventStack& eventStack)
override
{
switch (pass_)
{
case 1:
MatchEventStackInMemberFunction(eventStack, this,
&FunctionBottlenecks::OnStopInvocation);
break;

case 2:
MatchEventStackInMemberFunction(eventStack, this,
&FunctionBottlenecks::OnStopFunction);
break;

default:
break;
}

return AnalysisControl::CONTINUE;
}

AnalysisControl OnSimpleEvent(const EventStack& eventStack)
{
if (pass_ > 1) {
return AnalysisControl::CONTINUE;
}

MatchEventStackInMemberFunction(eventStack, this,
&FunctionBottlenecks::ProcessForceInlinee);

return AnalysisControl::CONTINUE;
}

void OnStopInvocation(Invocation invocation)
{
using namespace std::chrono;

// Ignore very short invocations
if (invocation.Duration() < std::chrono::seconds(1)) {
return;
}

cachedInvocationDurations_[invocation.EventInstanceId()] =
duration_cast<milliseconds>(invocation.Duration());
}

void OnStopFunction(Invocation invocation, Function func)
{
using namespace std::chrono;

auto itInvocation = cachedInvocationDurations_.find(
invocation.EventInstanceId());

if (itInvocation == cachedInvocationDurations_.end()) {
return;
}

auto itForceInlineSize = forceInlineSizeCache_.find(
func.EventInstanceId());

unsigned forceInlineSize =
itForceInlineSize == forceInlineSizeCache_.end() ?
0 : itForceInlineSize->second;

milliseconds functionMilliseconds =
duration_cast<milliseconds>(func.Duration());

double functionTime = static_cast<double>(
functionMilliseconds.count());

double invocationTime = static_cast<double>(
itInvocation->second.count());

double percent = functionTime / invocationTime;

if (percent > 0.05 && func.Duration() >= seconds(1))
{
identifiedFunctions_[func.EventInstanceId()]=
{ func.Name(), functionMilliseconds, percent,
forceInlineSize };
}
}

void ProcessForceInlinee(Function func, ForceInlinee inlinee)
{
forceInlineSizeCache_[func.EventInstanceId()] +=
inlinee.Size();
}

AnalysisControl OnEndAnalysis() override
{
std::vector<IdentifiedFunction> sortedFunctions;

for (auto& p : identifiedFunctions_) {
sortedFunctions.push_back(p.second);
}

std::sort(sortedFunctions.begin(), sortedFunctions.end());

for (auto& func : sortedFunctions)
{
bool forceInlineHeavy = func.ForceInlineeSize >= 10000;

std::string forceInlineIndicator = forceInlineHeavy ?
", *" : "";

int percent = static_cast<int>(func.Percent * 100);

std::string percentString = "(" +
std::to_string(percent) + "%" +
forceInlineIndicator + ")";

std::cout << std::setw(9) << std::right <<
func.Duration.count();
std::cout << " ms ";
std::cout << std::setw(9) << std::left <<
percentString;
std::cout << " " << func.Name << std::endl;
}

return AnalysisControl::CONTINUE;
}

private:
unsigned pass_;

std::unordered_map<unsigned long long,
std::chrono::milliseconds> cachedInvocationDurations_;

std::unordered_map<unsigned long long,
IdentifiedFunction> identifiedFunctions_;

std::unordered_map<unsigned long long,
unsigned> forceInlineSizeCache_;
};

int main(int argc, char* argv[])
{
if (argc <= 1) return -1;

std::cout.imbue(std::locale(""));

FunctionBottlenecks fb;

auto group = MakeStaticAnalyzerGroup(&fb);

// argv[1] should contain the path to a trace file
int numberOfPasses = 2;
return Analyze(argv[1], numberOfPasses, group);
}
4 changes: 4 additions & 0 deletions FunctionBottlenecks/packages.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Cpp.BuildInsights" version="1.1.0" targetFramework="native" />
</packages>
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This repository provides buildable and runnable samples for the C++ Build Insigh
| Sample | Description |
|-------------------|--------------------------------------------|
| BottleneckCompileFinder | Finds CL invocations that are bottlenecks and don't use /MP. |
| FunctionBottlenecks | Prints a list of functions that are code generation bottlenecks within their CL or Link invocation. |
| LongCodeGenFinder | Lists the functions that take more than 500 milliseconds to generate in your entire build. |
| RecursiveTemplateInspector | Identifies costly recursive template instantiations. |
| TopHeaders | Determines which headers you might want to precompile. |
Expand Down
Loading

0 comments on commit edc69ad

Please sign in to comment.