Skip to content
This repository has been archived by the owner on Jan 22, 2022. It is now read-only.

Commit

Permalink
Reduce CPU usage of AssetCompileWatcher
Browse files Browse the repository at this point in the history
- The FileSystemWatcher takes a significant amount of CPU time when watching many files. Split out the watcher into many file system watchers that watch only individual directories which contain .cs files. This works much better for CPU usage, but it still takes a relatively long time because the FileSystemWatcher polls for changes far too frequently and does not provide a mechanism to control its poll rate.
  • Loading branch information
MerlinVR committed Apr 10, 2020
1 parent 7f01386 commit 5d442c4
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 34 deletions.
98 changes: 65 additions & 33 deletions Assets/UdonSharp/Editor/UdonSharpAssetCompileWatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,70 @@ namespace UdonSharp
/// This has the downside that we expect the user to know what they're doing and have valid syntax that's getting fed into the compiler since there is no "real" compilation happening on the C#
/// But the benefit we get is that UdonSharp scripts compile nearly instantly.
/// So this whole class just exists to give people that option.
///
/// I may want to rewrite this eventually because the FileSystemWatcher polls updates too frequently and burns CPU for no reason. There is no way to slow down its internal polling as far as I know.
/// </summary>
[InitializeOnLoad]
public class UdonSharpAssetCompileWatcher
{
static FileSystemWatcher fileSystemWatcher;
static FileSystemWatcher[] fileSystemWatchers;
static readonly object modifiedFileLock = new object();

static HashSet<string> modifiedFilePaths = new HashSet<string>();
static HashSet<string> renamedFilePaths = new HashSet<string>();

static bool lastEnabledState = false;

static UdonSharpAssetCompileWatcher()
{
EditorApplication.update += OnEditorUpdate;
}

static void SetupWatchers()
{
if (fileSystemWatchers != null)
return;

AssemblyReloadEvents.beforeAssemblyReload += CleanupWatchers;

string[] directories = Directory.GetDirectories("Assets/", "*", SearchOption.AllDirectories);

List<string> sourceDirectories = new List<string>();

AssemblyReloadEvents.beforeAssemblyReload += CleanupWatcher;
foreach (string directory in directories)
{
if (Directory.GetFiles(directory, "*.cs").Length > 0)
sourceDirectories.Add(directory.Replace('\\', '/'));
}

fileSystemWatchers = new FileSystemWatcher[sourceDirectories.Count];

for (int i = 0; i < sourceDirectories.Count; ++i)
{
FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(sourceDirectories[i], "*.cs");
fileSystemWatcher.IncludeSubdirectories = false;
fileSystemWatcher.InternalBufferSize = 1024; // Someone would need to modify 64 files in a single directory at once to hit this

fileSystemWatcher = new FileSystemWatcher("Assets/", "*.cs");
fileSystemWatcher.IncludeSubdirectories = true;
fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
fileSystemWatcher.Changed += OnSourceFileChanged;

fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
fileSystemWatcher.Changed += OnSourceFileChanged;
//fileSystemWatcher.Renamed += OnSourceFileRenamed;
fileSystemWatcher.EnableRaisingEvents = true;
fileSystemWatchers[i] = fileSystemWatcher;
}
}

static void CleanupWatcher()
static void CleanupWatchers()
{
if (fileSystemWatcher != null)
if (fileSystemWatchers != null)
{
fileSystemWatcher.EnableRaisingEvents = false;
fileSystemWatcher.Changed -= OnSourceFileChanged;;
fileSystemWatcher.Dispose();
foreach (FileSystemWatcher fileSystemWatcher in fileSystemWatchers)
{
if (fileSystemWatcher != null)
{
fileSystemWatcher.EnableRaisingEvents = false;
fileSystemWatcher.Changed -= OnSourceFileChanged; ;
fileSystemWatcher.Dispose();
}
}
}
}

Expand Down Expand Up @@ -93,12 +125,30 @@ static void HandleScriptModifications(List<MonoScript> scripts)

static void OnEditorUpdate()
{
List<MonoScript> modifiedScripts = new List<MonoScript>();
SetupWatchers();

UdonSharpSettings settings = UdonSharpSettings.GetSettings();

bool watcherEnabled = settings == null || settings.autoCompileOnModify;

if (watcherEnabled != lastEnabledState && fileSystemWatchers != null)
{
lastEnabledState = watcherEnabled;
foreach (FileSystemWatcher watcher in fileSystemWatchers)
{
if (watcher != null)
watcher.EnableRaisingEvents = watcherEnabled;
}
}

List<MonoScript> modifiedScripts = null;

lock (modifiedFileLock)
{
if (modifiedFilePaths.Count > 0)
{
modifiedScripts = new List<MonoScript>();

foreach (string filePath in modifiedFilePaths)
{
MonoScript asset = AssetDatabase.LoadAssetAtPath<MonoScript>(filePath.Replace(Application.dataPath.Replace("/", "\\"), "Assets"));
Expand All @@ -109,18 +159,8 @@ static void OnEditorUpdate()
}
}

if (modifiedScripts.Count > 0)
if (modifiedScripts != null && modifiedScripts.Count > 0)
HandleScriptModifications(modifiedScripts);

//lock (modifiedFileLock)
//{
// foreach (string renamedFile in renamedFilePaths)
// {
// Debug.Log(renamedFile);
// }

// renamedFilePaths.Clear();
//}
}

static void OnSourceFileChanged(object source, FileSystemEventArgs args)
Expand All @@ -130,14 +170,6 @@ static void OnSourceFileChanged(object source, FileSystemEventArgs args)
modifiedFilePaths.Add(args.FullPath);
}
}

static void OnSourceFileRenamed(object source, RenamedEventArgs args)
{
//lock (modifiedFileLock)
//{
// renamedFilePaths.Add(args.Name);
//}
}
}

}
2 changes: 1 addition & 1 deletion Assets/UdonSharp/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.15.1
v0.15.3

0 comments on commit 5d442c4

Please sign in to comment.