Last active
August 29, 2015 14:14
-
-
Save vansha/e2359d568373bf51127d to your computer and use it in GitHub Desktop.
Measure and profile Visual Studio solution build performance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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