-
Notifications
You must be signed in to change notification settings - Fork 21
add some basic parallelism to improve performance #799
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,12 +22,14 @@ public override void Add (TLDefinition tld, Stream srcStm) | |
return; | ||
SwiftName className = ToClassName (tld); | ||
SwiftClassName formalName = ToFormalClassName (tld); | ||
ClassContents contents = null; | ||
if (!values.TryGetValue (className, out contents)) { | ||
contents = new ClassContents (formalName, sizeofMachinePointer); | ||
values.Add (className, contents); | ||
lock (valuesLock) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as with the ErrorHandler, we could use https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=net-8.0 Looking at the code on which you are locking, you could remove the need to the TryGetValue, use TryAdd which will add only if not present. If you were to update, TryUpdate would be the method you want. I don't know the code base, but you might be in a situation in which some Invetories are thread-safe and others are not. In this case, I cannot think of a drawback looking at your code AND will ensure that all subclasses are threadsafe. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. opened an issue for it. #800 |
||
ClassContents contents = null; | ||
if (!values.TryGetValue (className, out contents)) { | ||
contents = new ClassContents (formalName, sizeofMachinePointer); | ||
values.Add (className, contents); | ||
} | ||
contents.Add (tld, srcStm); | ||
} | ||
contents.Add (tld, srcStm); | ||
} | ||
|
||
public static SwiftClassName ToFormalClassName (TLDefinition tld) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,12 +22,14 @@ public override void Add (TLDefinition tld, Stream srcStm) | |
if (tlf == null) | ||
throw ErrorHelper.CreateError (ReflectorError.kInventoryBase + 10, $"expected a top-level function but got a {tld.GetType ().Name}"); | ||
|
||
OverloadInventory overloads = null; | ||
if (!values.TryGetValue (tlf.Name, out overloads)) { | ||
overloads = new OverloadInventory (tlf.Name, sizeofMachinePointer); | ||
values.Add (tlf.Name, overloads); | ||
lock (valuesLock) { | ||
OverloadInventory overloads = null; | ||
if (!values.TryGetValue (tlf.Name, out overloads)) { | ||
overloads = new OverloadInventory (tlf.Name, sizeofMachinePointer); | ||
values.Add (tlf.Name, overloads); | ||
} | ||
overloads.Add (tlf, srcStm); | ||
} | ||
Comment on lines
+25
to
32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same, which also means that changing the dictionary in the base type will make all of your subclasses thread-safe. |
||
overloads.Add (tlf, srcStm); | ||
} | ||
|
||
public TLFunction ContainsEquivalentFunction (TLFunction func) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
|
||
namespace SwiftReflector.Inventory { | ||
public abstract class Inventory<T> { | ||
protected object valuesLock = new object (); | ||
protected Dictionary<SwiftName, T> values = new Dictionary<SwiftName, T> (); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A ConcurrentDict will make your life easier. |
||
|
||
public IEnumerable<SwiftName> Names { get { return values.Keys; } } | ||
|
@@ -17,7 +18,9 @@ public abstract class Inventory<T> { | |
|
||
public bool ContainsName (SwiftName name) | ||
{ | ||
return values.ContainsKey (name); | ||
lock (valuesLock) { | ||
return values.ContainsKey (name); | ||
} | ||
} | ||
public bool ContainsName (string name) | ||
{ | ||
|
@@ -26,7 +29,9 @@ public bool ContainsName (string name) | |
|
||
public bool TryGetValue (SwiftName name, out T value) | ||
{ | ||
return values.TryGetValue (name, out value); | ||
lock (valuesLock) { | ||
return values.TryGetValue (name, out value); | ||
} | ||
} | ||
|
||
public bool TryGetValue (string name, out T value) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
using SwiftReflector.Demangling; | ||
using ObjCRuntime; | ||
using SwiftRuntimeLibrary; | ||
using System.Threading.Tasks; | ||
|
||
namespace SwiftReflector.Inventory { | ||
public class ModuleInventory : Inventory<ModuleContents> { | ||
|
@@ -24,12 +25,14 @@ public int SizeofMachinePointer { | |
|
||
public override void Add (TLDefinition tld, Stream srcStm) | ||
{ | ||
ModuleContents module = null; | ||
if (!values.TryGetValue (tld.Module, out module)) { | ||
module = new ModuleContents (tld.Module, SizeofMachinePointer); | ||
values.Add (tld.Module, module); | ||
lock (valuesLock) { | ||
ModuleContents module = null; | ||
if (!values.TryGetValue (tld.Module, out module)) { | ||
module = new ModuleContents (tld.Module, SizeofMachinePointer); | ||
values.Add (tld.Module, module); | ||
} | ||
module.Add (tld, srcStm); | ||
Comment on lines
+28
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same, all this methods are yy most of the time. |
||
} | ||
module.Add (tld, srcStm); | ||
} | ||
|
||
public IEnumerable<SwiftName> ModuleNames { | ||
|
@@ -111,6 +114,23 @@ public static ModuleInventory FromFile (string pathToDynamicLibrary, ErrorHandli | |
} | ||
} | ||
|
||
public static async Task<ModuleInventory> FromFilesAsync (IEnumerable<string> pathsToLibraryFiles, ErrorHandling errors) | ||
{ | ||
var streams = pathsToLibraryFiles.Select (path => new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.Read)).ToList (); | ||
if (streams.Count == 0) | ||
return null; | ||
var inventory = new ModuleInventory (); | ||
try { | ||
var tasks = streams.Select (stm => FromStreamIntoAsync (stm, inventory, errors, stm.Name)); | ||
await Task.WhenAll (tasks); | ||
} finally { | ||
foreach (var stm in streams) { | ||
stm.Dispose (); | ||
} | ||
Comment on lines
+127
to
+129
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Reactive Extensions (https://github.com/dotnet/reactive) provides a nice way to deal with a collection of IDisposables: https://learn.microsoft.com/en-us/previous-versions/dotnet/reactive-extensions/hh229918(v=vs.103) I don't know how you feel about adding it as a dependency "System.Reactive" but will probably help. |
||
} | ||
return inventory; | ||
} | ||
|
||
public static ModuleInventory FromFiles (IEnumerable<string> pathsToLibraryFiles, ErrorHandling errors) | ||
{ | ||
ModuleInventory inventory = null; | ||
|
@@ -124,6 +144,14 @@ public static ModuleInventory FromFiles (IEnumerable<string> pathsToLibraryFiles | |
return inventory; | ||
} | ||
|
||
static async Task<ModuleInventory> FromStreamIntoAsync (Stream stm, ModuleInventory inventory, | ||
ErrorHandling errors, string fileName = null) | ||
{ | ||
return await Task.Run (() => { | ||
return FromStreamInto (stm, inventory, errors, fileName); | ||
}); | ||
} | ||
|
||
static ModuleInventory FromStreamInto (Stream stm, ModuleInventory inventory, | ||
ErrorHandling errors, string fileName = null) | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe you can make your life easier by using https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentbag-1?view=net-8.0
You will need to make a decision, but looking at your code you can move from a List to a Bag since:
There is a few draw backs, one is that you won't have an AddRange method: dotnet/runtime#61291
The second one I can't think, by definition all LINQ operations are thread-safe because they only read, the diff is that the concurrent bag provides a snapshot of the collection while iterating, that will change the behavior and probably, if that must be kept, is best to use a lock.
You'll have to probably write that the class must be thread-safe for the future team.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
opened an issue for it #801