-
Notifications
You must be signed in to change notification settings - Fork 294
[Efficiency Improver] perf: eliminate yield-iterator state machine in PropertyBag.OfType(T)() #7917
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8e95fc5
911b697
813c705
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 |
|---|---|---|
|
|
@@ -274,9 +274,38 @@ public TProperty[] OfType<TProperty>() | |
| } | ||
|
|
||
| // We don't want to allocate an array if we know that we're looking for a TestNodeStateProperty | ||
| return typeof(TestNodeStateProperty).IsAssignableFrom(typeof(TProperty)) | ||
| if (typeof(TestNodeStateProperty).IsAssignableFrom(typeof(TProperty)) || _property is null) | ||
| { | ||
| return []; | ||
| } | ||
|
|
||
| // Direct linked-list walk: avoids allocating a yield-iterator state machine | ||
| // (the original code called _property.OfType<TProperty>() which uses yield return). | ||
| TProperty? first = default; | ||
| bool foundAny = false; | ||
| List<TProperty>? overflow = null; | ||
| Property? current = _property; | ||
| while (current is not null) | ||
| { | ||
| if (current.Current is TProperty match) | ||
| { | ||
| if (!foundAny) | ||
| { | ||
| first = match; | ||
| foundAny = true; | ||
| } | ||
| else | ||
| { | ||
| (overflow ??= [first!]).Add(match); | ||
| } | ||
| } | ||
|
|
||
| current = current.Next; | ||
| } | ||
|
|
||
| return !foundAny | ||
|
Contributor
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. [Coverage] The Impact: The new manual traversal diverges from the old LINQ-based Suggestion: Add one assertion to the existing // No DummyProperty2 in the bag — exercises the "no match found" path in the while-loop
Assert.IsEmpty(property.OfType<DummyProperty2>()); |
||
| ? [] | ||
| : _property is null ? [] : [.. _property.OfType<TProperty>()]; | ||
| : overflow is not null ? [.. overflow] : [first!]; | ||
|
Contributor
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. [Coverage] The single-item fast path ( Impact: A regression where a single-result bag returns Suggestion: Extend PropertyBag bag = new();
DummyProperty single = new();
bag.Add(single);
bag.Add(PassedTestNodeStateProperty.CachedInstance);
TProperty[] result = bag.OfType<DummyProperty>();
Assert.HasCount(1, result);
Assert.AreSame(single, result[0]); |
||
| } | ||
|
|
||
| /// <summary> | ||
|
|
||
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.
[Coverage] The
|| _property is nullbranch is new (the original ternary had_property is null ? []as its own arm). There's currently no test for callingOfType<SomeDummyProperty>()on aPropertyBagthat contains only aTestNodeStateProperty(i.e.,_testNodeStatePropertyis set but_property is null).Impact: The early-return path added here goes unverified. If a future refactor accidentally removes it, a silent linear walk over a
nullreference would produce incorrect results.Suggestion: Add a test case to
PropertyBagTests: