Enable parallelism in Crossgen2 and add flags for it in SuperIlc. (#495)
authorAnubhav Srivastava <SrivastavaAnubhav@users.noreply.github.com>
Fri, 6 Dec 2019 02:43:00 +0000 (18:43 -0800)
committerFadi Hanna <fadim@microsoft.com>
Fri, 6 Dec 2019 02:43:00 +0000 (18:43 -0800)
* Enable parallelism in Crossgen2 and add flags for it in SuperIlc.

* Use fluent api pattern in ReadyToRunCodegenCompilationBuilder for resilient, IBC tuning, and parallelism flags.

src/coreclr/src/tools/ReadyToRun.SuperIlc/BuildOptions.cs
src/coreclr/src/tools/ReadyToRun.SuperIlc/CommandLineOptions.cs
src/coreclr/src/tools/ReadyToRun.SuperIlc/CpaotRunner.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs
src/coreclr/src/tools/crossgen2/crossgen2/CommandLineOptions.cs
src/coreclr/src/tools/crossgen2/crossgen2/Program.cs

index b3d2277..6316fd3 100644 (file)
@@ -28,6 +28,7 @@ namespace ReadyToRun.SuperIlc
         public bool UseFramework { get; set; }
         public bool Release { get; set; }
         public bool LargeBubble { get; set; }
+        public int Crossgen2Parallelism { get; set; }
         public int CompilationTimeoutMinutes { get; set; }
         public int ExecutionTimeoutMinutes { get; set; }
         public DirectoryInfo[] ReferencePath { get; set; }
index 5f9d92f..67460be 100644 (file)
@@ -44,6 +44,7 @@ namespace ReadyToRun.SuperIlc
                         UseFramework(),
                         Release(),
                         LargeBubble(),
+                        Crossgen2Parallelism(),
                         ReferencePath(),
                         IssuesPath(),
                         CompilationTimeoutMinutes(),
@@ -75,6 +76,7 @@ namespace ReadyToRun.SuperIlc
                         UseFramework(),
                         Release(),
                         LargeBubble(),
+                        Crossgen2Parallelism(),
                         ReferencePath(),
                         IssuesPath(),
                         CompilationTimeoutMinutes(),
@@ -195,6 +197,9 @@ namespace ReadyToRun.SuperIlc
             Option LargeBubble() =>
                 new Option(new[] { "--large-bubble" }, "Assume all input files as part of one version bubble", new Argument<bool>());
 
+            Option Crossgen2Parallelism() =>
+                new Option(new[] { "--crossgen2-parallelism" }, "Max number of threads to use in Crossgen2 (default = logical processor count)", new Argument<int>());
+
             Option IssuesPath() =>
                 new Option(new[] { "--issues-path", "-ip" }, "Path to issues.targets", new Argument<FileInfo[]>() { Arity = ArgumentArity.ZeroOrMore });
 
index b8498b6..cce5f42 100644 (file)
@@ -25,7 +25,11 @@ namespace ReadyToRun.SuperIlc
 
         public CpaotRunner(BuildOptions options, IEnumerable<string> referencePaths)
             : base(options, referencePaths)
-        { }
+        {
+            // Set SuperIlc parallelism to a low enough value that ensures that each Crossgen2 invocation gets to use its parallelism
+            if (options.DegreeOfParallelism == 0)
+                options.DegreeOfParallelism = 2;
+        }
 
         protected override ProcessParameters ExecutionProcess(IEnumerable<string> modules, IEnumerable<string> folders, bool noEtw)
         {
@@ -55,6 +59,11 @@ namespace ReadyToRun.SuperIlc
                 yield return "--inputbubble";
             }
 
+            if (_options.Crossgen2Parallelism != 0)
+            {
+                yield return $"--parallelism={_options.Crossgen2Parallelism}";
+            }
+
             foreach (var reference in ComputeManagedAssemblies.GetManagedAssembliesInFolder(Path.GetDirectoryName(assemblyFileName)))
             {
                 yield return $"-r:{reference}";
index 25c0650..503fa4f 100644 (file)
@@ -8,6 +8,7 @@ using System.IO;
 using System.Reflection.PortableExecutable;
 using System.Runtime.CompilerServices;
 using System.Threading;
+using System.Threading.Tasks;
 
 using Internal.IL;
 using Internal.IL.Stubs;
@@ -190,12 +191,20 @@ namespace ILCompiler
         private readonly JitConfigProvider _jitConfigProvider;
 
         /// <summary>
+        /// We only need one CorInfoImpl per thread, and we don't want to unnecessarily construct them
+        /// because their construction takes a significant amount of time.
+        /// </summary>
+        private readonly ConditionalWeakTable<Thread, CorInfoImpl> _corInfoImpls;
+
+        /// <summary>
         /// Name of the compilation input MSIL file.
         /// </summary>
         private readonly string _inputFilePath;
 
         private bool _resilient;
 
+        private int _parallelism;
+
         public new ReadyToRunCodegenNodeFactory NodeFactory { get; }
 
         public ReadyToRunSymbolNodeFactory SymbolNodeFactory { get; }
@@ -210,16 +219,19 @@ namespace ILCompiler
             JitConfigProvider configProvider,
             string inputFilePath,
             IEnumerable<ModuleDesc> modulesBeingInstrumented,
-            bool resilient)
+            bool resilient,
+            int parallelism)
             : base(dependencyGraph, nodeFactory, roots, ilProvider, devirtualizationManager, modulesBeingInstrumented, logger)
         {
             _resilient = resilient;
+            _parallelism = parallelism;
             NodeFactory = nodeFactory;
             SymbolNodeFactory = new ReadyToRunSymbolNodeFactory(nodeFactory);
             _jitConfigProvider = configProvider;
 
             _inputFilePath = inputFilePath;
 
+            _corInfoImpls = new ConditionalWeakTable<Thread, CorInfoImpl>();
             CorInfoImpl.RegisterJITModule(configProvider);
         }
 
@@ -264,8 +276,11 @@ namespace ILCompiler
         {
             using (PerfEventSource.StartStopEvents.JitEvents())
             {
-                ConditionalWeakTable<Thread, CorInfoImpl> cwt = new ConditionalWeakTable<Thread, CorInfoImpl>();
-                foreach (DependencyNodeCore<NodeFactory> dependency in obj)
+                ParallelOptions options = new ParallelOptions
+                {
+                    MaxDegreeOfParallelism = _parallelism
+                };
+                Parallel.ForEach(obj, options, dependency =>
                 {
                     MethodWithGCInfo methodCodeNodeNeedingCode = dependency as MethodWithGCInfo;
                     MethodDesc method = methodCodeNodeNeedingCode.Method;
@@ -280,7 +295,7 @@ namespace ILCompiler
                     {
                         using (PerfEventSource.StartStopEvents.JitMethodEvents())
                         {
-                            CorInfoImpl corInfoImpl = cwt.GetValue(Thread.CurrentThread, thread => new CorInfoImpl(this));
+                            CorInfoImpl corInfoImpl = _corInfoImpls.GetValue(Thread.CurrentThread, thread => new CorInfoImpl(this));
                             corInfoImpl.CompileMethod(methodCodeNodeNeedingCode);
                         }
                     }
@@ -297,7 +312,7 @@ namespace ILCompiler
                     {
                         Logger.Writer.WriteLine($"Warning: Method `{method}` was not compiled because `{ex.Message}` requires runtime JIT");
                     }
-                }
+                });
             }
 
             if (_methodILCache.Count > 1000)
index 0475c2c..5b1376b 100644 (file)
@@ -20,8 +20,9 @@ namespace ILCompiler
     {
         private readonly string _inputFilePath;
         private readonly EcmaModule _inputModule;
-        private readonly bool _ibcTuning;
-        private readonly bool _resilient;
+        private bool _ibcTuning;
+        private bool _resilient;
+        private int _parallelism;
         private string _jitPath;
 
         // These need to provide reasonable defaults so that the user can optionally skip
@@ -29,13 +30,10 @@ namespace ILCompiler
         private KeyValuePair<string, string>[] _ryujitOptions = Array.Empty<KeyValuePair<string, string>>();
         private ILProvider _ilProvider = new ReadyToRunILProvider();
 
-        public ReadyToRunCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group, string inputFilePath, bool ibcTuning, bool resilient)
+        public ReadyToRunCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group, string inputFilePath)
             : base(context, group, new CoreRTNameMangler())
         {
             _inputFilePath = inputFilePath;
-            _ibcTuning = ibcTuning;
-            _resilient = resilient;
-
             _inputModule = context.GetModuleFromPath(_inputFilePath);
 
             // R2R field layout needs compilation group information
@@ -84,6 +82,25 @@ namespace ILCompiler
             return this;
         }
 
+        public ReadyToRunCodegenCompilationBuilder UseIbcTuning(bool ibcTuning)
+        {
+            _ibcTuning = ibcTuning;
+            return this;
+        }
+
+        public ReadyToRunCodegenCompilationBuilder UseResilience(bool resilient)
+        {
+            _resilient = resilient;
+            return this;
+        }
+
+        public ReadyToRunCodegenCompilationBuilder UseParallelism(int parallelism)
+        {
+            _parallelism = parallelism;
+            return this;
+        }
+
+
         public override ICompilation ToCompilation()
         {
             ModuleTokenResolver moduleTokenResolver = new ModuleTokenResolver(_compilationGroup, _context);
@@ -167,7 +184,8 @@ namespace ILCompiler
                 jitConfig,
                 _inputFilePath,
                 new ModuleDesc[] { _inputModule },
-                _resilient);
+                _resilient,
+                _parallelism);
         }
     }
 }
index c943984..61dd2a0 100644 (file)
@@ -31,6 +31,8 @@ namespace ILCompiler
         public bool Tuning { get; set; }
         public bool Partial { get; set; }
         public bool Resilient { get; set; }
+        public int Parallelism { get; set; }
+
 
         public string SingleMethodTypeName { get; set; }
         public string SingleMethodName { get; set; }
@@ -140,6 +142,10 @@ namespace ILCompiler
                     // We don't need to override arity here as 255 is the maximum number of generic arguments
                     Argument = new Argument<string[]>()
                 },
+                new Option(new[] { "--parallelism" }, "Maximum number of threads to use during compilation")
+                { 
+                    Argument = new Argument<int>(() => Environment.ProcessorCount)
+                },
             };
         }
     }
index 4979d23..d0a7758 100644 (file)
@@ -311,10 +311,7 @@ namespace ILCompiler
                         inputFilePath = input.Value;
                         break;
                     }
-                    CompilationBuilder builder = new ReadyToRunCodegenCompilationBuilder(typeSystemContext, compilationGroup, inputFilePath,
-                        ibcTuning: _commandLineOptions.Tuning,
-                        resilient: _commandLineOptions.Resilient);
-
+                    ReadyToRunCodegenCompilationBuilder builder = new ReadyToRunCodegenCompilationBuilder(typeSystemContext, compilationGroup, inputFilePath);
                     string compilationUnitPrefix = "";
                     builder.UseCompilationUnitPrefix(compilationUnitPrefix);
 
@@ -324,6 +321,9 @@ namespace ILCompiler
                         DependencyTrackingLevel.None : (_commandLineOptions.GenerateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First);
 
                     builder
+                        .UseIbcTuning(_commandLineOptions.Tuning)
+                        .UseResilience(_commandLineOptions.Resilient)
+                        .UseParallelism(_commandLineOptions.Parallelism)
                         .UseILProvider(ilProvider)
                         .UseJitPath(_commandLineOptions.JitPath)
                         .UseBackendOptions(_commandLineOptions.CodegenOptions)