-
Notifications
You must be signed in to change notification settings - Fork 275
[AI Task] [Tizen.NUI.Components] Remove unrealizedItems temp list in Linear/GridLayouter scroll path#7644
[AI Task] [Tizen.NUI.Components] Remove unrealizedItems temp list in Linear/GridLayouter scroll path #7644JoonghyunCho wants to merge 2 commits into
Conversation
...r/GridLayouter Inline the unrealize check directly into VisibleItems.RemoveAll predicate in four scroll hot-path / NotifyItemRangeRemoved sites: - LinearLayouter.cs (RequestLayout, NotifyItemRangeRemoved) - GridLayouter.cs (RequestLayout, NotifyItemRangeRemoved) Before: allocated a temporary List<RecyclerViewItem> per call and used RemoveAll(list.Contains) (O(N*M) linear scan on each predicate call). After: single O(N) RemoveAll pass with zero allocation per call. No public API change. Behavior preserved. Fixes #7608
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.
Code Review
This pull request optimizes the removal of invisible or removed items in GridLayouter and LinearLayouter by replacing temporary list allocations and O(N*M) lookups with the RemoveAll method. While this improves performance to O(N), the review highlights that the current implementation still incurs heap allocations due to closure captures in the lambdas. To achieve true zero allocation on these performance-critical paths, a manual iteration and shifting approach was suggested.
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.
While this change significantly improves performance by moving from O(×ばつM) to O(N), it doesn't strictly achieve "zero per-call allocation" as stated in the PR description. In C#, a lambda that captures local variables or this (which happens here via FirstVisible, LastVisible, and collectionView) results in a heap allocation for a closure object and a delegate instance on every call.
Since RequestLayout is on the scroll hot path, you could achieve true zero allocation by using a manual for loop to shift elements in-place, followed by a RemoveRange call. However, RemoveAll is much more idiomatic and readable, so this is only worth changing if absolute zero allocation is required for this performance-critical path.
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.
Similar to the change in GridLayouter, this RemoveAll call involves a closure allocation because the lambda captures state. To completely eliminate heap pressure on the scroll hot path, a manual iteration and shifting approach would be required. Given the PR's focus on eliminating allocations, this is a relevant detail to consider.
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.
🤖 [AI Review]
🟡 Suggestion: The lambda still captures startIndex, count, and collectionView, forcing a per-call closure allocation — so the PR's stated "zero per-call allocation" goal is not fully achieved here. A reverse-iteration for loop with RemoveAt eliminates both the temp list and the closure with no behavior change.
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.
🤖 [AI Review]
Addressed in 03a8955 — replaced RemoveAll(lambda) with a reverse for loop using RemoveAt, eliminating both the temp list and the closure allocation on this path.
Replace RemoveAll(lambda) with reverse for + RemoveAt in four Linear/GridLayouter sites to eliminate the per-call closure allocation noted by reviewers, fully realizing the PR's "zero per-call allocation" goal on the scroll hot path. Applied-Human-Comments: 3254514563,3254514566 Applied-AI-Comments: 3255636218
JoonghyunCho
commented
May 18, 2026
🤖 [AI Review]
Addressed review feedback in commit 03a8955. Summary: replaced the four VisibleItems.RemoveAll(lambda) sites in LinearLayouter/GridLayouter (RequestLayout + NotifyItemRangeRemoved) with a reverse for + RemoveAt loop, eliminating the per-call closure allocation.
Summary
Removes the temporary
List<RecyclerViewItem>allocation and the O(×ばつM)RemoveAll(list.Contains)pattern from four scroll/range-removed sites inLinearLayouterandGridLayouter. The unrealize check is inlined directly into theRemoveAllpredicate, giving a single O(N) pass with zero per-call allocation on the scroll hot path.Changes
src/Tizen.NUI.Components/Controls/RecyclerView/Layouter/LinearLayouter.csRequestLayout(line ~422): replaced foreach-into-temp-list +RemoveAll(list.Contains)with inline-predicateRemoveAll.NotifyItemRangeRemoved(line ~1137): same replacement; theitem.Index -= countshift is folded into the same predicate.src/Tizen.NUI.Components/Controls/RecyclerView/Layouter/GridLayouter.csRequestLayout(line ~419): same replacement.NotifyItemRangeRemoved(line ~1032): same replacement.Mode
Refactoring
Performance impact (per call)
List<>allocationRemoveAllpredicateVisibleItemsRequestLayoutis called many times per second on scroll, so this directly removes per-frame Gen0 pressure on the recycler hot path.API compatibility
Index -= countshift for items above the removed range.Verification
dotnet build src/Tizen.NUI.Components— 0 errors; pre-existing warnings unchanged).sdbdevice available in this environment — manual on-device benchmark recommended).Fixes #7608