Skip to content

Instantly share code, notes, and snippets.

@vansha
Last active August 29, 2015 14:14
Show Gist options
  • Save vansha/e2359d568373bf51127d to your computer and use it in GitHub Desktop.
Save vansha/e2359d568373bf51127d to your computer and use it in GitHub Desktop.
Measure and profile Visual Studio solution build performance
// Struggling with slow VS solution build?
// This script should help in analyzing slow parts, by higlighting slowest projects and msbuild targets.
//
// Use following steps:
// 1. Setup proper Visual studio build settings. Use TOOLS -> Options -> Projects and Solutions -> Build and Run dialog.
// * maximum number of parallel project builds - 1
// * MSBuild project output verbosity - Diagnostic. In Diagnostic mode MSBuild outputs targets performance summary for each project.
// 2. Clean solution
// 3. Build solution
// 4. Copy build output from Output window to some file, for example c:\tmp\build-output.txt
// 5. Run this script using LinqPad utility (http://www.linqpad.net/)
// 6. Analyze output.
const string buildLog = @"c:\tmp\build-output.txt";
void Main()
{
var collector = new StatisticsCollector();
foreach (var line in File.ReadLines(buildLog))
collector.Visit(line);
var totalTime = TimeSpan.FromMilliseconds(collector.TargetsPerformace.Sum(x => x.TargetTimeMs)).TotalSeconds;
Console.WriteLine("Total build time: {0} seconds.", totalTime);
// Per target statistics
var perTargetStat = collector.TargetsPerformace
.GroupBy(x => x.Target)
.Select(x => new
{
Target = x.Key,
TimeMs = x.Sum(row => row.TargetTimeMs),
TopSlowProjects = x
.Select(row => new { Project = row.Project, TimeMs = row.TargetTimeMs })
.OrderByDescending(t => t.TimeMs)
.Take(15),
})
.OrderByDescending(x => x.TimeMs)
.Where(x => x.TimeMs >= 50)
.ToList();
Console.WriteLine("Statistics per target (targets that slower than 150 ms):");
perTargetStat.Dump();
// Per project statistics
var perProjectStat = collector.TargetsPerformace
.GroupBy(x => x.Project)
.Select(x => new
{
Project = x.Key,
TimeMs = x.Sum(row => row.TargetTimeMs),
TopSlowTargets = x
.Select(row => new { Target = row.Target, TimeMs = row.TargetTimeMs })
.OrderByDescending(t => t.TimeMs)
.Take(6),
})
.OrderByDescending(x => x.TimeMs)
.ToList();
Console.WriteLine("Statistics per project:");
perProjectStat.Dump();
// ToJiraTable(perTargetStat).Dump();
// ToJiraTable(perProjectStat).Dump();
}
public class StatisticsCollector
{
static readonly Regex taskPerformanceRegex = new Regex(@"\s*(\d*)\s*[m][s]\s*(\w*).*");
bool insideTasksSection;
string currentProject;
public readonly List<TargetTime> TargetsPerformace = new List<TargetTime>();
public void Visit(string line)
{
// Parsing following line
// ------ Build started: Project: Wilco.Data.Test, Configuration: Debug Any CPU ------
if (line.StartsWith("------ ") && line.Contains("started: Project: "))
ParseProject(line);
if (line == "Target Performance Summary:")
{
insideTasksSection = true;
return;
}
if (insideTasksSection)
{
if (line.Trim() == "")
{
insideTasksSection = false;
}
else
{
ParseTask(line);
}
}
}
void ParseTask(string line)
{
var match = taskPerformanceRegex.Match(line);
int timeMs = int.Parse(match.Groups[1].Value);
string taskName = match.Groups[2].Value;
TargetsPerformace.Add(new TargetTime { Project = currentProject, Target = taskName, TargetTimeMs = timeMs });
}
void ParseProject(string line)
{
// To lazy for regexp
var placeholder = "started: Project: ";
var projectNameStart = line.IndexOf(placeholder) + placeholder.Length;
var projectNameEnd = line.IndexOf(',', projectNameStart);
currentProject = line.Substring(projectNameStart, projectNameEnd - projectNameStart);
}
}
public class TargetTime
{
public string Project;
public string Target;
public int TargetTimeMs;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment