I wanted to use Profile Guided Optimization (PGO) for my project, but I have the VS 2015 Community Edition which does not have PGO UI integration. My project has a solution file and the solution file has many other sub-projects within it.

I ended up making a batch file for the instrumentation and a powershell script for optimizing. I also created two property files to override the compile and link options. Let me start with the batch file that performs the instrumentation. A key thing to remember if your solution has multiple projects is that all of the projects must create their executables in one single directory. The output of the linker across all projects must go to this one directory directly, not via a post-build event.

Batch file for instrumenting

msbuild %1 /p:Configuration=%2 /t:Clean
msbuild %1 /p:Configuration=%2 /p:ForceImportBeforeCppTargets=%~dp0\pginstrument.props /maxcpucount:8

This batch file should be invoked from the VS 2015 command prompt. Make sure the one for the right architecture is launched. Invoke this batch file as

pginstrument.bat MySolution.sln Release

The batch file takes the solution file name as the first parameter(%1) and the configuration as the second(%2). It begins by cleaning the solution. The ForceImportBeforeCppTargets overrides a compile option and linker options. The %~dp0 instructs msbuild to load the pginstrument.props from the same directory that the batch file was invoked from. The option for maxcpucount merely instructs msbuild to use parallel builds. At the end of the build there will be a file with the extension pgd for each executable file.

Property file for instrumenting (pginstrument.props)

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemDefinitionGroup>
        <ClCompile>
            <WholeProgramOptimization>true</WholeProgramOptimization>
        </ClCompile>
        <Link>
            <AdditionalOptions>/INCREMENTAL:NO /GENPROFILE /LTCG:PGInstrument %(AdditionalOptions)</AdditionalOptions>
        </Link>
    </ItemDefinitionGroup>
</Project>

The property file uses the WholeProgramOptimization tag to set the /GL flag for the C++ compiler (without this flag the compiler will not be able to instrument the code). It also adds some self-evident linker options.

Run test scenarios

Once the instrumenting build is done, start up the instrumented application and run through the various application scenarios that need to be optimized. This will create files with the pgc extension for all the DLLs and EXEs that were executed. These files will then be used by the linker to create optimized binaries.

Powershell script for optimizing

param([string]$src, [string]$config = "RelWithDebInfo", [string]$maxcpu = "8")

Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser

Get-ChildItem $src -Recurse -Filter *.vcxproj | Where-Object {$_.Name -notlike "INSTALL*"} | ForEach-Object { 
    $exe = 'msbuild'
    $arg1 = $_.FullName
    $arg2 = "/p:Configuration=${config}"
    $arg3 = "/p:ForceImportBeforeCppTargets=${PSScriptRoot}\pgoptimize.props"
    $arg4 = "/t:BuildLink"
    $arg5 = "/maxcpucount:${maxcpu}"

    &$exe $arg1 $arg2 $arg3 $arg4 $arg5
}

In the optimize step, only the linker step needs to be run. Unfortunately, I could not find a way to run msbuild on the solution file and only perform the linker step. I could perform the linker step on the individual projects though. However, I have a lot of projects and it seemed tedious to run the optimize step for each one manually. So, I wrote a script that would walk the various project subdirectories, generate and execute the msbuild statement to optimize. I also use CMake that generates install projects which do not need to be optimized. It’s easier to filter those out in the powershell script. This script file, again, should be invoked from the VS 2015 command prompt. Invoke this script as

powershell pgoptimizer.ps1 -src MySolutionDir -config Release

Property file for optimizing (pgoptimize.props)

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemDefinitionGroup>
        <PreBuildEvent>
            <Message></Message>
            <Command></Command>
        </PreBuildEvent>
        <Link>
            <AdditionalOptions>/INCREMENTAL:NO /USEPROFILE /LTCG:PGOptimize %(AdditionalOptions)</AdditionalOptions>
        </Link>
        <PostBuildEvent>
            <Message></Message>
            <Command></Command>
        </PostBuildEvent>
    </ItemDefinitionGroup>
</Project>

The property file prevents the pre and post build events from running. It also adds linker options to use the pgc files and optimize the binaries. Test the optimized binaries once they are built.