Browse Source

Explicitly create partitioners for `Parallel.ForEach` calls.

Apparently the default `Parallel.ForEach` logic checks whether the input enumerable is an array or `IList<T>`,
and if it is, creates whole chunks of sequential list/array indices and assigns those to the worker threads.

This is more efficient if the individual items are processed very quickly; but if they take varying amounts of time, a single chunk full of expensive items might keep a single CPU core busy for long after all other CPU cores have gone idle.
pull/2087/head
Daniel Grunwald 5 years ago
parent
commit
d02fd09822
  1. 3
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
  2. 6
      ILSpy/Commands/DecompileAllCommand.cs
  3. 7
      ILSpy/Commands/DisassembleAllCommand.cs
  4. 7
      ILSpy/SolutionWriter.cs

3
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs

@ -33,6 +33,7 @@ using static ICSharpCode.Decompiler.Metadata.MetadataExtensions; @@ -33,6 +33,7 @@ using static ICSharpCode.Decompiler.Metadata.MetadataExtensions;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.DebugInfo;
using System.Collections.Concurrent;
namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{
@ -205,7 +206,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -205,7 +206,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
var progress = ProgressIndicator;
DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver, Settings);
Parallel.ForEach(
files,
Partitioner.Create(files, loadBalance: true),
new ParallelOptions {
MaxDegreeOfParallelism = this.MaxDegreeOfParallelism,
CancellationToken = cancellationToken

6
ILSpy/Commands/DecompileAllCommand.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
#if DEBUG
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
@ -40,7 +41,10 @@ namespace ICSharpCode.ILSpy @@ -40,7 +41,10 @@ namespace ICSharpCode.ILSpy
{
Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput();
Parallel.ForEach(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate(LoadedAssembly asm) {
Parallel.ForEach(
Partitioner.Create( MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), loadBalance: true),
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
delegate(LoadedAssembly asm) {
if (!asm.HasLoadError) {
Stopwatch w = Stopwatch.StartNew();
Exception exception = null;

7
ILSpy/Commands/DisassembleAllCommand.cs

@ -23,6 +23,8 @@ using System.Diagnostics; @@ -23,6 +23,8 @@ using System.Diagnostics;
using System.Threading.Tasks;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.Properties;
using System.Collections.Concurrent;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(Menu = nameof(Resources._File), Header = nameof(Resources.DEBUGDisassemble), MenuCategory = nameof(Resources.Open), MenuOrder = 2.5)]
@ -37,7 +39,10 @@ namespace ICSharpCode.ILSpy @@ -37,7 +39,10 @@ namespace ICSharpCode.ILSpy
{
Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput();
Parallel.ForEach(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate(LoadedAssembly asm) {
Parallel.ForEach(
Partitioner.Create(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), loadBalance: true),
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
delegate (LoadedAssembly asm) {
if (!asm.HasLoadError) {
Stopwatch w = Stopwatch.StartNew();
Exception exception = null;

7
ILSpy/SolutionWriter.cs

@ -99,7 +99,12 @@ namespace ICSharpCode.ILSpy @@ -99,7 +99,12 @@ namespace ICSharpCode.ILSpy
Stopwatch stopwatch = Stopwatch.StartNew();
try {
await Task.Run(() => Parallel.ForEach(assemblies, n => WriteProject(n, language, solutionDirectory, ct)))
// Explicitly create an enumerable partitioner here to avoid Parallel.ForEach's special cases for lists,
// as those seem to use static partitioning which is inefficient if assemblies take differently
// long to decompile.
await Task.Run(() => Parallel.ForEach(Partitioner.Create(assemblies),
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
n => WriteProject(n, language, solutionDirectory, ct)))
.ConfigureAwait(false);
await Task.Run(() => SolutionCreator.WriteSolutionFile(solutionFilePath, projects))

Loading…
Cancel
Save