Skip to content

Materialize trigger collections to eliminate ConcatIterator CPU waste#215

Open
PhenX wants to merge 1 commit intomasterfrom
fix/concat-iterator-perf
Open

Materialize trigger collections to eliminate ConcatIterator CPU waste#215
PhenX wants to merge 1 commit intomasterfrom
fix/concat-iterator-perf

Conversation

@PhenX
Copy link
Member

@PhenX PhenX commented Mar 21, 2026

…r CPU waste

Replace IEnumerable fields with List to avoid deeply nested ConcatIterator chains from repeated .Concat() calls. Each WithAdditionalTrigger call now uses List.Add() instead. Also cache the service provider hash code, use O(1) List.Count property instead of LINQ .Count(), and add count-based fast paths in ShouldUseSameServiceProvider to short-circuit before SequenceEqual.

(cherry picked from commit 0cc6aa1)

…r CPU waste

Replace IEnumerable<T> fields with List<T> to avoid deeply nested
ConcatIterator chains from repeated .Concat() calls. Each WithAdditionalTrigger
call now uses List.Add() instead. Also cache the service provider hash code,
use O(1) List.Count property instead of LINQ .Count(), and add count-based
fast paths in ShouldUseSameServiceProvider to short-circuit before SequenceEqual.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 0cc6aa1)
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes TriggersOptionExtension to avoid CPU overhead from repeatedly chaining IEnumerable.Concat() (creating deep ConcatIterator trees) by materializing trigger collections into List<T>, and adds a couple of small fast paths around service-provider caching comparisons.

Changes:

  • Replace trigger registries from IEnumerable<T> to List<T> and switch WithAdditional* methods to List.Add().
  • Cache GetServiceProviderHashCode() result and use List.Count / count-based short-circuiting in ShouldUseSameServiceProvider().
  • Avoid LINQ .Count() in debug info population.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +128 to +129
private List<(object typeOrInstance, ServiceLifetime lifetime)>? _triggers;
private List<Type> _triggerTypes;
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing _triggers to List<...> means the public Triggers property now returns a reference to a mutable internal collection (it’s exposed as IEnumerable, but consumers can still cast back to List and mutate). That can invalidate the new GetServiceProviderHashCode() cache and break the expectation that option extensions are effectively immutable. Consider returning a read-only wrapper (AsReadOnly()), an IReadOnlyList, or otherwise preventing external mutation.

Copilot uses AI. Check for mistakes.
clone._triggerTypes = clone._triggerTypes.Concat(triggerTypesEnumerable);
}

clone._triggerTypes ??= new List<Type>();
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_triggerTypes is non-nullable and always initialized in constructors, so clone._triggerTypes ??= new List<Type>(); is redundant and suggests the field can be null. Either remove the null-coalescing assignment here, or (if null is a valid state) make _triggerTypes nullable consistently and handle that throughout.

Suggested change
clone._triggerTypes ??= new List<Type>();

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants