refactor: Discard internal use of FastList#2798
Conversation
|
If this change is approved, I will replace all the fastlists as much as possible :) |
|
Small note, the idea would be to replace all codepaths referencing the internal array directly with the CollectionsMarshal.AsSpan(List) to avoid performance degradation. for (int i = 0; i < keyFrames.Count; ++i)
{
if (parentNodeIndex == 0)
keyFrames.Items[i].Value -= PivotPosition;
keyFrames.Items[i].Value *= ScaleImport;
}You would do var span = CollectionsMarshal.AsSpan(keyFrames);
for (int i = 0; i < span.Count; ++i)
{
if (parentNodeIndex == 0)
span[i].Value -= PivotPosition;
span[i].Value *= ScaleImport;
} |
thanks , i see ,i will change |
This reverts commit 7d72ba4.
|
|
||
| public class AnimationCurveEvaluatorDirectFloatGroup : AnimationCurveEvaluatorDirectBlittableGroupBase<float> | ||
| { | ||
| protected unsafe override void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, IntPtr location) |
There was a problem hiding this comment.
BTW, I Adjusted the location with unsafe and unsafe
| Sort(collection.Items, 0, collection.Count, comparer); | ||
| } | ||
|
|
||
| public static void Sort<T>(FastList<T> collection, IComparer<T> comparer) |
There was a problem hiding this comment.
I delete this, because the method is 0 used , and if deleted , the scripts will easier to maintain , if not it will cause misunderstandings to others
There was a problem hiding this comment.
Dispatcher is public, users could be calling this method in their project, so we should leave it for for now and remove it once we remove FastList<T>
| currentRenderTargetsNonMSAA.Clear(); | ||
| currentRenderTargetsNonMSAA.Capacity = currentRenderTargets.Count; | ||
|
|
There was a problem hiding this comment.
I think:
.Resize =.Clear() + set Capacity
There was a problem hiding this comment.
Unfortunately, this case isn't equivalent. In FastList<T> if the new capacity was smaller than the previous one, only its size was modified (i.e. no allocations). In List<T> in all cases (except when it's already the correct size) a new array is allocated and a copy occurs.
This will have impact on performance and was one of the reason FastList<T> was created.
Compare
stride/sources/core/Stride.Core/Collections/FastList.cs
Lines 220 to 232 in b2c29fd
with
https://github.com/dotnet/runtime/blob/2765f1856d7ebcc0ebd9708e7a2322f565a1fc77/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs#L97-L124
There was a problem hiding this comment.
List<T>.EnsureCapacity might be better.
|
I should have time to review this next week end if Kryptos doesn't do it by then |
Yes , thanks, I maybe try it , the key question is use more modern and better ways to improve performance, rather than just delete code :) 👍 |
|
👀 |
Eideren
left a comment
There was a problem hiding this comment.
Sorry about the wait, pretty good work overall, just a few things to fix and some improvements you could tackle.
| var keyFrames = ((AnimationCurve<Vector3>)curve).KeyFrames; | ||
| var keyFramesSpan = CollectionsMarshal.AsSpan(keyFrames); | ||
| for (int i = 0; i < keyFrames.Count; ++i) | ||
| { | ||
| if (parentNodeIndex == 0) | ||
| keyFrames.Items[i].Value -= PivotPosition; | ||
| keyFrames.Items[i].Value *= ScaleImport; | ||
| keyFramesSpan[i].Value -= PivotPosition; | ||
| keyFramesSpan[i].Value *= ScaleImport; | ||
| } |
There was a problem hiding this comment.
keyFrames.Count -> keyFramesSpan.Length, using the span's length instead of the list's will allow dotnet to elide the bounds check, see https://sharplab.io/#v2:C4LghgzgtgPgAgJgIwFgBQcAMACOSAsA3OlrkgHQBKArgHbACWUApuQJL3MBOA9gA4BlbgDcGAY2YRiaEgGZcCbAGF0Ab3TZNueQ3rYAsgAoAMgwjAAPADMANjzDAAfNhsAabLfvAPASg1b1NC1g7GEwLmwIPjBabABeZR4bG2YxRh5aCH1wiAALMBtyAEEIAWjaQxsfaX8Qjx4Iw11vBnjsTEJsVosXciUeOmBOgGphhj8guuxAqZCGK2xDKJiAbQYAXXiEqwnZ4Jm9urgAdi7pQ80AX1qQ68m6m+CT7ABaJHOtO5u4HT19BBMZksngczjcHjsDl8NwOdTCEWWsQS/WSqXSmWyXDyBWKpXKlWq6EeWisDUWzS6bQ6lJ6iPIxmYtAA5sBciMxrsprC9vNFoi1ps4ttOYduRdcKcGB9DndZrKpsTNM83tLsHdLkA=
| currentRenderTargetsNonMSAA.Resize(currentRenderTargets.Count, false); | ||
| currentRenderTargetsNonMSAA.Clear(); | ||
| currentRenderTargetsNonMSAA.EnsureCapacity(currentRenderTargets.Count); | ||
|
|
There was a problem hiding this comment.
This isn't quite the same, right now, the way you have it setup is that you're clearing the whole list and then making sure the list can contain at least currentRenderTargets.Count long.
Resize changes the list's capacity to hold the given amount, but also the list's length, so if it is 5 items long and you pass it 3, it will be 3 items long from then on, but it's capacity will still be 5 or whichever capacity the list had at the time.
And passing false to Resize only affects the list when decreasing its length, so when the list is 5 items in length and is changed to 3 items in length, the 4th and 5th item will be set to null/default.
| currentRenderTargets.Resize(renderTargets.Count, false); | ||
| currentRenderTargets.Clear(); | ||
| currentRenderTargets.EnsureCapacity(renderTargets.Count); |
There was a problem hiding this comment.
Same as the other comment
| var resourceBindingsSpan = CollectionsMarshal.AsSpan(reflection.ResourceBindings); | ||
|
|
||
| // prepare resource bindings used internally | ||
| for (int i = 0; i < reflection.ResourceBindings.Count; i++) |
There was a problem hiding this comment.
See my other comment about about using the span's length directly
| renderTargets.Resize(validatedTargetCount, false); | ||
|
|
||
| renderTargets.Clear(); | ||
| renderTargets.EnsureCapacity(renderTargets.Count); |
| if (parameterKeyInfos[i].Key == layoutParameterKeyInfo.Key) | ||
| { | ||
| processedParameters[i] = true; | ||
| newParameterKeyInfos.Items[i] = layoutParameterKeyInfo; | ||
| newParameterKeyInfosSpan[i] = layoutParameterKeyInfo; |
There was a problem hiding this comment.
parameterKeyInfos.Count -> newParameterKeyInfosSpan.Length
| var parameterKeyInfosSpan = CollectionsMarshal.AsSpan(parameterKeyInfos); | ||
| // Find existing first | ||
| for (int i = 0; i < parameterKeyInfos.Count; ++i) | ||
| { | ||
| if (parameterKeyInfos.Items[i].Key == parameterKey) | ||
| if (parameterKeyInfosSpan[i].Key == parameterKey) | ||
| { | ||
| return parameterKeyInfos.Items[i].GetObjectAccessor(); | ||
| return parameterKeyInfosSpan[i].GetObjectAccessor(); |
There was a problem hiding this comment.
Same as other span length stuff
|
|
||
| private unsafe bool IsTetrahedronAllocated(int index) | ||
| private unsafe bool IsTetrahedronAllocated(int index, Span<Tetrahedron> tetrahedronSpan) | ||
| { | ||
| fixed (Tetrahedron* tetrahedron = &tetrahedralization.Items[index]) | ||
| fixed (Tetrahedron* tetrahedron = &tetrahedronSpan[index]) | ||
| { | ||
| return tetrahedron->Vertices[0] != -1; | ||
| } | ||
| } |
There was a problem hiding this comment.
Entire method body can be replaced by return tetrahedronSpan[index].Vertices[0] != -1;
| // Mark it as "unused" | ||
| fixed (Tetrahedron* tetrahedron = &tetrahedralization.Items[index]) | ||
| fixed (Tetrahedron* tetrahedron = &tetrahedronSpan[index]) | ||
| { | ||
| tetrahedron->Vertices[0] = -1; | ||
|
|
There was a problem hiding this comment.
tetrahedronSpan[index].Vertices[0] = -1;
| { | ||
| var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); | ||
| // Check connectivity | ||
| fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) | ||
| fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) | ||
| { | ||
| for (int index = 0; index < tetrahedralization.Count; index++) | ||
| { | ||
| if (!IsTetrahedronAllocated(index)) | ||
| if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) |
Replaces inefficient Clear() calls and Count-based loops with span-based operations using CollectionsMarshal.AsSpan for better performance. Updates affected code in ForwardRenderer, RenderOutputValidator, ImportModelCommand.Animation, and Effect to use spans for clearing and iterating over lists, and adds an obsolete overload in Dispatcher to guide usage towards array-based sorting.
Refactored BowyerWatsonTetrahedralization to eliminate unsafe code and pointer arithmetic, replacing them with direct access to Span and ref variables. This improves code safety, readability, and maintainability while preserving the original logic and performance.
Replaced Count-based loops with span Length in ParameterCollection for improved performance and correctness. Removed unnecessary Clear() call on SortedRenderNodes in RenderSystem as the collection is properly cleared in Reset().
|
Tweaked it a bit before merging, thanks for your work @Arc-huangjingtong |

PR Details
[FastList] is already obsolete ,but still has some code in project ,In the long run, someone has to do this
Related Issue
#2332
Types of changes
Checklist