Skip to content

[Performance] O(rules × references) nested loop in GetViolationsFrom for ProjectPath/ProjectTag detectors #54

@mfogliatto

Description

@mfogliatto

Description

ProjectPathViolationDetector.GetViolationsFrom() and ProjectTagViolationDetector.GetViolationsFrom() both iterate over all rules in the outer loop and all references in the inner loop, resulting in O(rules × references) complexity.

Unlike AssemblyNameViolationDetector (which has an experimental O(n) path using dictionary lookups), these detectors have no optimized path — the GetViolationsFromExperimental() just delegates back to the same nested loop.

Affected Files

  • src/ReferenceCop/Detectors/ProjectPathViolationDetector.csGetViolationsFrom()
  • src/ReferenceCop/Detectors/ProjectTagViolationDetector.csGetViolationsFrom()

Impact

For projects with many references (e.g., 50+ project references) and many rules, the nested iteration becomes costly, especially during MSBuild where this runs per-project.

Suggested Optimization

For ProjectTagViolationDetector: Pre-filter rules that match the current project's tag (only one fromProjectTag can match). Then use a HashSet or dictionary keyed by ToProjectTag for O(1) lookup per reference:

var matchingRules = this.rules.Where(r => r.FromProjectTag == fromProjectTag).ToList();
var blockedTags = new HashSet<string>(matchingRules.Select(r => r.ToProjectTag));

foreach (var referenceContext in references)
{
    var toTag = this.projectTagProvider.GetProjectTag(referenceContext.Reference);
    if (blockedTags.Contains(toTag) && !referenceContext.IsWarningSuppressed)
    {
        var rule = matchingRules.First(r => r.ToProjectTag == toTag);
        yield return new Violation(rule, referenceContext.Reference);
    }
}

For ProjectPathViolationDetector: Similar approach — pre-filter rules matching the current project's path prefix, then check references linearly against only the matching subset.

Metadata

Metadata

Assignees

No one assigned

    Labels

    performancePerformance optimization

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions