From 0a653057c75351eab4b7f0e24a4a04a4d0aacd8f Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Wed, 26 Dec 2018 12:58:07 -0800 Subject: [PATCH 1/4] optimize AnalysisHashSet.SetEquals a bit --- src/Analysis/Engine/Impl/AnalysisHashSet.cs | 23 ++++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/Analysis/Engine/Impl/AnalysisHashSet.cs b/src/Analysis/Engine/Impl/AnalysisHashSet.cs index a612be922..c39c69233 100644 --- a/src/Analysis/Engine/Impl/AnalysisHashSet.cs +++ b/src/Analysis/Engine/Impl/AnalysisHashSet.cs @@ -275,7 +275,15 @@ public bool SetEquals(IAnalysisSet other) { return true; } + if (Count == 0 && other.Count == 0) { + return true; + } + if (Comparer == other.Comparer) { + if (Count != other.Count) { + return false; + } + // Quick check for any unmatched hashcodes. // This can conclusively prove the sets are not equal, but cannot // prove equality. @@ -290,17 +298,16 @@ public bool SetEquals(IAnalysisSet other) { } } - var otherHc = new HashSet(other, _comparer); - foreach (var key in this) { - if (!otherHc.Remove(key)) { - return false; - } - } - if (otherHc.Any()) { + if (Count == 0 || other.Count == 0) { return false; } - return true; + if (Count == 1 && other.Count == 1) { + return _comparer.Equals(this.First(), other.First()); + } + + var hs = new HashSet(other, _comparer); + return hs.SetEquals(this); } /// From 3a7a3870ead1f9c6a2f9fb2ae63a441d49826ced Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 27 Dec 2018 12:33:37 -0800 Subject: [PATCH 2/4] enforce single instances of comparers --- src/Analysis/Engine/Impl/AnalysisSet.cs | 4 +++- src/Analysis/Engine/Impl/Intellisense/AnalysisQueue.cs | 2 +- src/Analysis/Engine/Impl/LocationInfo.cs | 6 +++++- src/Analysis/Engine/Impl/OverloadResult.cs | 4 ++-- src/Analysis/Engine/Impl/PythonAnalyzer.cs | 4 +++- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Analysis/Engine/Impl/AnalysisSet.cs b/src/Analysis/Engine/Impl/AnalysisSet.cs index efd8a5a38..821f8ba7c 100644 --- a/src/Analysis/Engine/Impl/AnalysisSet.cs +++ b/src/Analysis/Engine/Impl/AnalysisSet.cs @@ -534,6 +534,8 @@ public static bool ContainsAny(this IAnalysisSet set, IAnalysisSet values) { sealed class ObjectComparer : IEqualityComparer, IEqualityComparer { public static readonly ObjectComparer Instance = new ObjectComparer(); + private ObjectComparer() { } + public bool Equals(AnalysisValue x, AnalysisValue y) { #if FULL_VALIDATION if (x != null && y != null) { @@ -574,7 +576,7 @@ sealed class UnionComparer : IEqualityComparer, IEqualityComparer public readonly int Strength; - public UnionComparer(int strength = 0) { + private UnionComparer(int strength = 0) { Strength = strength; } diff --git a/src/Analysis/Engine/Impl/Intellisense/AnalysisQueue.cs b/src/Analysis/Engine/Impl/Intellisense/AnalysisQueue.cs index 143e66ed8..085572ac4 100644 --- a/src/Analysis/Engine/Impl/Intellisense/AnalysisQueue.cs +++ b/src/Analysis/Engine/Impl/Intellisense/AnalysisQueue.cs @@ -206,7 +206,7 @@ public async Task Handler(CancellationToken cancellationToken) { } private sealed class QueueItemComparer : IEqualityComparer { - public static IEqualityComparer Instance { get; } = new QueueItemComparer(); + public static readonly IEqualityComparer Instance = new QueueItemComparer(); private QueueItemComparer() { } public bool Equals(QueueItem x, QueueItem y) => Equals(x.Key, y.Key); diff --git a/src/Analysis/Engine/Impl/LocationInfo.cs b/src/Analysis/Engine/Impl/LocationInfo.cs index c34854ea6..78eac9caa 100644 --- a/src/Analysis/Engine/Impl/LocationInfo.cs +++ b/src/Analysis/Engine/Impl/LocationInfo.cs @@ -83,9 +83,13 @@ public bool Equals(ILocationInfo other) { /// Provides an IEqualityComparer that compares line, column and project entries. By /// default locations are equaitable based upon only line/project entry. /// - public static IEqualityComparer FullComparer { get; } = new FullLocationComparer(); + public static IEqualityComparer FullComparer => FullLocationComparer.Instance; sealed class FullLocationComparer : IEqualityComparer, IEqualityComparer { + public static readonly FullLocationComparer Instance = new FullLocationComparer(); + + private FullLocationComparer() { } + public bool Equals(LocationInfo x, LocationInfo y) => Equals(x, y); public bool Equals(ILocationInfo x, ILocationInfo y) { return x.StartLine == y.StartLine && diff --git a/src/Analysis/Engine/Impl/OverloadResult.cs b/src/Analysis/Engine/Impl/OverloadResult.cs index b7f9f2a1e..9af018318 100644 --- a/src/Analysis/Engine/Impl/OverloadResult.cs +++ b/src/Analysis/Engine/Impl/OverloadResult.cs @@ -425,8 +425,8 @@ internal ParameterResult GetParameterResultFromParameterInfo(IParameterInfo para } class OverloadResultComparer : EqualityComparer { - public static IEqualityComparer Instance = new OverloadResultComparer(false); - public static IEqualityComparer WeakInstance = new OverloadResultComparer(true); + public static readonly IEqualityComparer Instance = new OverloadResultComparer(false); + public static readonly IEqualityComparer WeakInstance = new OverloadResultComparer(true); private readonly bool _weak; diff --git a/src/Analysis/Engine/Impl/PythonAnalyzer.cs b/src/Analysis/Engine/Impl/PythonAnalyzer.cs index c8b90d753..d6654ba58 100644 --- a/src/Analysis/Engine/Impl/PythonAnalyzer.cs +++ b/src/Analysis/Engine/Impl/PythonAnalyzer.cs @@ -921,7 +921,9 @@ private AggregateProjectEntry GetAggregateWorker(IProjectEntry[] all) { } class AggregateComparer : IEqualityComparer { - public static AggregateComparer Instance = new AggregateComparer(); + public static readonly AggregateComparer Instance = new AggregateComparer(); + + private AggregateComparer() { } public bool Equals(IProjectEntry[] x, IProjectEntry[] y) { if (x.Length != y.Length) { From 75d47d1504f59702a11905d10883c31867484b83 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 27 Dec 2018 15:39:03 -0800 Subject: [PATCH 3/4] restore early return on first MRO result --- src/Analysis/Engine/Impl/Values/ClassInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Analysis/Engine/Impl/Values/ClassInfo.cs b/src/Analysis/Engine/Impl/Values/ClassInfo.cs index ef6e14ec1..b313f334f 100644 --- a/src/Analysis/Engine/Impl/Values/ClassInfo.cs +++ b/src/Analysis/Engine/Impl/Values/ClassInfo.cs @@ -831,6 +831,10 @@ public static IAnalysisSet GetMemberFromMroNoReferences(IEnumerable 0) { + break; + } } return result; } From 93249e6e4170802060a4446dd115d094767cc772 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 28 Dec 2018 10:25:44 -0800 Subject: [PATCH 4/4] optimize count accesses --- src/Analysis/Engine/Impl/AnalysisHashSet.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Analysis/Engine/Impl/AnalysisHashSet.cs b/src/Analysis/Engine/Impl/AnalysisHashSet.cs index c39c69233..65143d677 100644 --- a/src/Analysis/Engine/Impl/AnalysisHashSet.cs +++ b/src/Analysis/Engine/Impl/AnalysisHashSet.cs @@ -275,12 +275,16 @@ public bool SetEquals(IAnalysisSet other) { return true; } - if (Count == 0 && other.Count == 0) { + // Only access Count once to prevent wasted time in bucket locks. + var lCount = Count; + var rCount = other.Count; + + if (lCount == 0 && rCount == 0) { return true; } if (Comparer == other.Comparer) { - if (Count != other.Count) { + if (lCount != rCount) { return false; } @@ -298,11 +302,11 @@ public bool SetEquals(IAnalysisSet other) { } } - if (Count == 0 || other.Count == 0) { + if (lCount == 0 || rCount == 0) { return false; } - if (Count == 1 && other.Count == 1) { + if (lCount == 1 && rCount == 1) { return _comparer.Equals(this.First(), other.First()); }