Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Find out what assemblies are being built and how long each takes.
using System;
using System.Collections.Generic;
using System.Text;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
[InitializeOnLoad]
public class AsmdefDebug
{
const string AssemblyReloadEventsEditorPref = "AssemblyReloadEventsTime";
const string AssemblyCompilationEventsEditorPref = "AssemblyCompilationEvents";
static readonly int ScriptAssembliesPathLen = "Library/ScriptAssemblies/".Length;
static Dictionary<string, DateTime> s_StartTimes = new Dictionary<string, DateTime>();
static StringBuilder s_BuildEvents = new StringBuilder();
static double s_CompilationTotalTime;
static AsmdefDebug()
{
CompilationPipeline.assemblyCompilationStarted += CompilationPipelineOnAssemblyCompilationStarted;
CompilationPipeline.assemblyCompilationFinished += CompilationPipelineOnAssemblyCompilationFinished;
AssemblyReloadEvents.beforeAssemblyReload += AssemblyReloadEventsOnBeforeAssemblyReload;
AssemblyReloadEvents.afterAssemblyReload += AssemblyReloadEventsOnAfterAssemblyReload;
}
static void CompilationPipelineOnAssemblyCompilationStarted(string assembly)
{
s_StartTimes[assembly] = DateTime.UtcNow;
}
static void CompilationPipelineOnAssemblyCompilationFinished(string assembly, CompilerMessage[] arg2)
{
var time = s_StartTimes[assembly];
var timeSpan = DateTime.UtcNow - s_StartTimes[assembly];
s_CompilationTotalTime += timeSpan.TotalMilliseconds;
s_BuildEvents.AppendFormat("{0:0.00}s {1}\n", timeSpan.TotalMilliseconds / 1000f, assembly.Substring(ScriptAssembliesPathLen, assembly.Length - ScriptAssembliesPathLen));
}
static void AssemblyReloadEventsOnBeforeAssemblyReload()
{
s_BuildEvents.AppendFormat("compilation total: {0:0.00}s\n", s_CompilationTotalTime / 1000f);
EditorPrefs.SetString(AssemblyReloadEventsEditorPref, DateTime.UtcNow.ToBinary().ToString());
EditorPrefs.SetString(AssemblyCompilationEventsEditorPref, s_BuildEvents.ToString());
}
static void AssemblyReloadEventsOnAfterAssemblyReload()
{
var binString = EditorPrefs.GetString(AssemblyReloadEventsEditorPref);
long bin = 0;
if (long.TryParse(binString, out bin))
{
var date = DateTime.FromBinary(bin);
var time = DateTime.UtcNow - date;
var compilationTimes = EditorPrefs.GetString(AssemblyCompilationEventsEditorPref);
if (!string.IsNullOrEmpty(compilationTimes))
{
Debug.Log("Compilation Report\n" + compilationTimes + "Assembly Reload Time: " + time.TotalSeconds + "s\n");
}
}
}
}
@angularsen

This comment has been minimized.

Copy link

@angularsen angularsen commented Sep 22, 2018

Some changes in my fork: https://gist.github.com/angularsen/7a48f47beb0f8a65dd786ec38b02da57

  • See diff: https://www.diffchecker.com/menVDXfa
  • Add #if UNITY_EDITOR, I wanted to have this in my ThirdParty folder and Editor special folders don't work with .asmdef files
  • Print total time in first line of log output, so you can see it without highlighting the log line in the console window
  • Remove unused code

Thanks for this, very useful! I found that I spent 1-2 seconds building test files and I very rarely run my tests, rather want to run those explicitly or as part of a build agent.

@karljj1

This comment has been minimized.

Copy link
Owner Author

@karljj1 karljj1 commented May 27, 2019

Worth noting that the compilation time value is a cumulation of compilation performed on multiple threads, so it's not the time it took from start to finish.

@aybe

This comment has been minimized.

Copy link

@aybe aybe commented May 3, 2020

@karljj1 I don't understand why you complicated all of your code ? would you mind explaining ?

This, OTOH, is simple and effective:

using System.Collections.Generic;
using System.Diagnostics;
using UnityEditor;
using UnityEditor.Compilation;
using Debug = UnityEngine.Debug;

[InitializeOnLoad]
internal static class CompilationTime
{
    private static readonly Dictionary<string, Stopwatch> Dictionary;
    private static readonly Stopwatch                     Stopwatch;

    static CompilationTime()
    {
        CompilationPipeline.compilationStarted          += OnCompilationStarted;
        CompilationPipeline.compilationFinished         += OnCompilationFinished;
        CompilationPipeline.assemblyCompilationStarted  += OnAssemblyCompilationStarted;
        CompilationPipeline.assemblyCompilationFinished += OnAssemblyCompilationFinished;
        Dictionary                                      =  new Dictionary<string, Stopwatch>();
        Stopwatch                                       =  new Stopwatch();
    }

    private static void OnCompilationStarted(object context)
    {
        Dictionary.Clear();
        Stopwatch.Start();
    }

    private static void OnCompilationFinished(object context)
    {
        var elapsed = Stopwatch.Elapsed;

        Stopwatch.Stop();
        Stopwatch.Reset();

        foreach (var pair in Dictionary)
        {
            Debug.Log($"Assembly {pair.Key.Replace("Library/ScriptAssemblies/", string.Empty)} " +
                      $"built in {pair.Value.Elapsed.TotalSeconds:F} seconds.");
        }

        Debug.Log($"Total compilation time: {elapsed.TotalSeconds:F} seconds.");
    }

    private static void OnAssemblyCompilationStarted(string value)
    {
        Dictionary.Add(value, Stopwatch.StartNew());
    }

    private static void OnAssemblyCompilationFinished(string value, CompilerMessage[] messages)
    {
        Dictionary[value].Stop();
    }
}
@karljj1

This comment has been minimized.

Copy link
Owner Author

@karljj1 karljj1 commented May 3, 2020

@aybe
I'm sure there are parts that can be made simpler however compilationStarted and compilationFinished did not exist when I wrote this(I actually requested them when I was writing it).

@aybe

This comment has been minimized.

Copy link

@aybe aybe commented May 3, 2020

okay I understand now, thanks 😃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.