Skip to content

Commit 51e4954

Browse files
authored
forward-port from release/6.0-rc2: MonoAOTCompiler: detect when nothing has changed, a… (#59597)
…nd skip any precompiling (#58979) Refactor to allow fast-path implement fast-path for MonoAOTCompiler when nothing has changed re-enable some tests that got disabled by mistake (cherry picked from commit 9fd17b1)
1 parent 9d075ae commit 51e4954

File tree

1 file changed

+102
-44
lines changed

1 file changed

+102
-44
lines changed

src/tasks/AotCompilerTask/MonoAOTCompiler.cs

Lines changed: 102 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -357,47 +357,77 @@ private bool ExecuteInternal()
357357

358358
_cache = new FileCache(CacheFilePath, Log);
359359

360-
//FIXME: check the nothing changed at all case
360+
List<PrecompileArguments> argsList = new();
361+
foreach (var assemblyItem in _assembliesToCompile)
362+
argsList.Add(GetPrecompileArgumentsFor(assemblyItem, monoPaths));
361363

362364
_totalNumAssemblies = _assembliesToCompile.Count;
363-
int allowedParallelism = Math.Min(_assembliesToCompile.Count, Environment.ProcessorCount);
364-
if (BuildEngine is IBuildEngine9 be9)
365-
allowedParallelism = be9.RequestCores(allowedParallelism);
366-
367-
if (DisableParallelAot || allowedParallelism == 1)
365+
if (CheckAllUpToDate(argsList))
368366
{
369-
foreach (var assemblyItem in _assembliesToCompile)
370-
{
371-
if (!PrecompileLibrarySerial(assemblyItem, monoPaths))
372-
return !Log.HasLoggedErrors;
373-
}
367+
Log.LogMessage(MessageImportance.High, "Everything is up-to-date, nothing to precompile");
368+
369+
_fileWrites.AddRange(argsList.SelectMany(args => args.ProxyFiles).Select(pf => pf.TargetFile));
370+
foreach (var args in argsList)
371+
compiledAssemblies.GetOrAdd(args.AOTAssembly.ItemSpec, args.AOTAssembly);
374372
}
375373
else
376374
{
377-
ParallelLoopResult result = Parallel.ForEach(
378-
_assembliesToCompile,
379-
new ParallelOptions { MaxDegreeOfParallelism = allowedParallelism },
380-
(assemblyItem, state) => PrecompileLibraryParallel(assemblyItem, monoPaths, state));
375+
int allowedParallelism = Math.Min(_assembliesToCompile.Count, Environment.ProcessorCount);
376+
if (BuildEngine is IBuildEngine9 be9)
377+
allowedParallelism = be9.RequestCores(allowedParallelism);
381378

382-
if (!result.IsCompleted)
379+
if (DisableParallelAot || allowedParallelism == 1)
383380
{
384-
return false;
381+
foreach (var args in argsList)
382+
{
383+
if (!PrecompileLibrarySerial(args))
384+
return !Log.HasLoggedErrors;
385+
}
385386
}
387+
else
388+
{
389+
ParallelLoopResult result = Parallel.ForEach(
390+
argsList,
391+
new ParallelOptions { MaxDegreeOfParallelism = allowedParallelism },
392+
(args, state) => PrecompileLibraryParallel(args, state));
393+
394+
if (!result.IsCompleted)
395+
{
396+
return false;
397+
}
398+
}
399+
400+
int numUnchanged = _totalNumAssemblies - _numCompiled;
401+
if (numUnchanged > 0 && numUnchanged != _totalNumAssemblies)
402+
Log.LogMessage(MessageImportance.High, $"[{numUnchanged}/{_totalNumAssemblies}] skipped unchanged assemblies.");
386403
}
387404

388-
int numUnchanged = _totalNumAssemblies - _numCompiled;
389-
if (numUnchanged > 0 && numUnchanged != _totalNumAssemblies)
390-
Log.LogMessage(MessageImportance.High, $"[{numUnchanged}/{_totalNumAssemblies}] skipped unchanged assemblies.");
405+
CompiledAssemblies = ConvertAssembliesDictToOrderedList(compiledAssemblies, _assembliesToCompile).ToArray();
391406

392407
if (_cache.Save(CacheFilePath!))
393408
_fileWrites.Add(CacheFilePath!);
394-
395-
CompiledAssemblies = ConvertAssembliesDictToOrderedList(compiledAssemblies, _assembliesToCompile).ToArray();
396409
FileWrites = _fileWrites.ToArray();
397410

398411
return !Log.HasLoggedErrors;
399412
}
400413

414+
private bool CheckAllUpToDate(IList<PrecompileArguments> argsList)
415+
{
416+
foreach (var args in argsList)
417+
{
418+
// compare original assembly vs it's outputs.. all it's outputs!
419+
string assemblyPath = args.AOTAssembly.GetMetadata("FullPath");
420+
if (args.ProxyFiles.Any(pf => IsNewerThanOutput(assemblyPath, pf.TargetFile)))
421+
return false;
422+
}
423+
424+
return true;
425+
426+
static bool IsNewerThanOutput(string inFile, string outFile)
427+
=> !File.Exists(inFile) || !File.Exists(outFile) ||
428+
(File.GetLastWriteTimeUtc(inFile) > File.GetLastWriteTimeUtc(outFile));
429+
}
430+
401431
private IList<ITaskItem> EnsureAndGetAssembliesInTheSameDir(ITaskItem[] originalAssemblies)
402432
{
403433
List<ITaskItem> filteredAssemblies = new();
@@ -454,7 +484,7 @@ static bool ShouldSkip(ITaskItem asmItem)
454484
=> bool.TryParse(asmItem.GetMetadata("AOT_InternalForceToInterpret"), out bool skip) && skip;
455485
}
456486

457-
private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
487+
private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, string? monoPaths)
458488
{
459489
string assembly = assemblyItem.GetMetadata("FullPath");
460490
string assemblyDir = Path.GetDirectoryName(assembly)!;
@@ -463,7 +493,6 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
463493
var processArgs = new List<string>();
464494
bool isDedup = assembly == DedupAssembly;
465495
List<ProxyFile> proxyFiles = new(capacity: 5);
466-
string msgPrefix = $"[{Path.GetFileName(assembly)}] ";
467496

468497
var a = assemblyItem.GetMetadata("AotArguments");
469498
if (a != null)
@@ -674,16 +703,26 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
674703
sw.WriteLine(responseFileContent);
675704
}
676705

677-
string workingDir = assemblyDir;
706+
return new PrecompileArguments(ResponseFilePath: responseFilePath,
707+
EnvironmentVariables: envVariables,
708+
WorkingDir: assemblyDir,
709+
AOTAssembly: aotAssembly,
710+
ProxyFiles: proxyFiles);
711+
}
678712

713+
private bool PrecompileLibrary(PrecompileArguments args)
714+
{
715+
string assembly = args.AOTAssembly.GetMetadata("FullPath");
679716
try
680717
{
718+
string msgPrefix = $"[{Path.GetFileName(assembly)}] ";
719+
681720
// run the AOT compiler
682721
(int exitCode, string output) = Utils.TryRunProcess(Log,
683722
CompilerBinaryPath,
684-
$"--response=\"{responseFilePath}\"",
685-
envVariables,
686-
workingDir,
723+
$"--response=\"{args.ResponseFilePath}\"",
724+
args.EnvironmentVariables,
725+
args.WorkingDir,
687726
silent: true,
688727
debugMessageImportance: MessageImportance.Low,
689728
label: Path.GetFileName(assembly));
@@ -692,9 +731,9 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
692731
// Log the command in a compact format which can be copy pasted
693732
{
694733
StringBuilder envStr = new StringBuilder(string.Empty);
695-
foreach (KeyValuePair<string, string> kvp in envVariables)
734+
foreach (KeyValuePair<string, string> kvp in args.EnvironmentVariables)
696735
envStr.Append($"{kvp.Key}={kvp.Value} ");
697-
Log.LogMessage(importance, $"{msgPrefix}Exec (with response file contents expanded) in {workingDir}: {envStr}{CompilerBinaryPath} {responseFileContent}");
736+
Log.LogMessage(importance, $"{msgPrefix}Exec (with response file contents expanded) in {args.WorkingDir}: {envStr}{CompilerBinaryPath} {File.ReadAllText(args.ResponseFilePath)}");
698737
}
699738

700739
Log.LogMessage(importance, output);
@@ -713,66 +752,66 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
713752
}
714753
finally
715754
{
716-
File.Delete(responseFilePath);
755+
File.Delete(args.ResponseFilePath);
717756
}
718757

719758
bool copied = false;
720-
foreach (var proxyFile in proxyFiles)
759+
foreach (var proxyFile in args.ProxyFiles)
721760
{
722761
copied |= proxyFile.CopyOutputFileIfChanged();
723762
_fileWrites.Add(proxyFile.TargetFile);
724763
}
725764

726765
if (copied)
727766
{
728-
string copiedFiles = string.Join(", ", proxyFiles.Select(tf => Path.GetFileName(tf.TargetFile)));
767+
string copiedFiles = string.Join(", ", args.ProxyFiles.Select(tf => Path.GetFileName(tf.TargetFile)));
729768
int count = Interlocked.Increment(ref _numCompiled);
730769
Log.LogMessage(MessageImportance.High, $"[{count}/{_totalNumAssemblies}] {Path.GetFileName(assembly)} -> {copiedFiles}");
731770
}
732771

733-
compiledAssemblies.GetOrAdd(aotAssembly.ItemSpec, aotAssembly);
772+
compiledAssemblies.GetOrAdd(args.AOTAssembly.ItemSpec, args.AOTAssembly);
734773
return true;
735774
}
736775

737-
private bool PrecompileLibrarySerial(ITaskItem assemblyItem, string? monoPaths)
776+
private bool PrecompileLibrarySerial(PrecompileArguments args)
738777
{
739778
try
740779
{
741-
if (PrecompileLibrary(assemblyItem, monoPaths))
780+
if (PrecompileLibrary(args))
742781
return true;
743782
}
744783
catch (LogAsErrorException laee)
745784
{
746-
Log.LogError($"Precompile failed for {assemblyItem}: {laee.Message}");
785+
Log.LogError($"Precompile failed for {args.AOTAssembly}: {laee.Message}");
747786
}
748787
catch (Exception ex)
749788
{
750789
if (Log.HasLoggedErrors)
751-
Log.LogMessage(MessageImportance.Low, $"Precompile failed for {assemblyItem}: {ex}");
790+
Log.LogMessage(MessageImportance.Low, $"Precompile failed for {args.AOTAssembly}: {ex}");
752791
else
753-
Log.LogError($"Precompile failed for {assemblyItem}: {ex}");
792+
Log.LogError($"Precompile failed for {args.AOTAssembly}: {ex}");
754793
}
755794

756795
return false;
757796
}
758797

759-
private void PrecompileLibraryParallel(ITaskItem assemblyItem, string? monoPaths, ParallelLoopState state)
798+
private void PrecompileLibraryParallel(PrecompileArguments args, ParallelLoopState state)
760799
{
761800
try
762801
{
763-
if (PrecompileLibrary(assemblyItem, monoPaths))
802+
if (PrecompileLibrary(args))
764803
return;
765804
}
766805
catch (LogAsErrorException laee)
767806
{
768-
Log.LogError($"Precompile failed for {assemblyItem}: {laee.Message}");
807+
Log.LogError($"Precompile failed for {args.AOTAssembly}: {laee.Message}");
769808
}
770809
catch (Exception ex)
771810
{
772811
if (Log.HasLoggedErrors)
773-
Log.LogMessage(MessageImportance.Low, $"Precompile failed for {assemblyItem}: {ex}");
812+
Log.LogMessage(MessageImportance.Low, $"Precompile failed for {args.AOTAssembly}: {ex}");
774813
else
775-
Log.LogError($"Precompile failed for {assemblyItem}: {ex}");
814+
Log.LogError($"Precompile failed for {args.AOTAssembly}: {ex}");
776815
}
777816

778817
state.Break();
@@ -908,6 +947,24 @@ private static IList<ITaskItem> ConvertAssembliesDictToOrderedList(ConcurrentDic
908947
}
909948
return outItems;
910949
}
950+
951+
internal class PrecompileArguments
952+
{
953+
public PrecompileArguments(string ResponseFilePath, IDictionary<string, string> EnvironmentVariables, string WorkingDir, ITaskItem AOTAssembly, IList<ProxyFile> ProxyFiles)
954+
{
955+
this.ResponseFilePath = ResponseFilePath;
956+
this.EnvironmentVariables = EnvironmentVariables;
957+
this.WorkingDir = WorkingDir;
958+
this.AOTAssembly = AOTAssembly;
959+
this.ProxyFiles = ProxyFiles;
960+
}
961+
962+
public string ResponseFilePath { get; private set; }
963+
public IDictionary<string, string> EnvironmentVariables { get; private set; }
964+
public string WorkingDir { get; private set; }
965+
public ITaskItem AOTAssembly { get; private set; }
966+
public IList<ProxyFile> ProxyFiles { get; private set; }
967+
}
911968
}
912969

913970
internal class FileCache
@@ -1017,6 +1074,7 @@ public bool CopyOutputFileIfChanged()
10171074
File.Delete(TempFile);
10181075
}
10191076
}
1077+
10201078
}
10211079

10221080
public enum MonoAotMode

0 commit comments

Comments
 (0)