Skip to content

Commit fbd61ec

Browse files
TEMP: Optimize via RenderTreeArrayBuilder
1 parent 145af7f commit fbd61ec

File tree

6 files changed

+122
-37
lines changed

6 files changed

+122
-37
lines changed

src/Components/Components/src/ParameterView.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,16 @@ public readonly struct ParameterView
1919
private const string GeneratedParameterViewElementName = "__ARTIFICIAL_PARAMETER_VIEW";
2020
private static readonly RenderTreeFrame[] _emptyFrames = new RenderTreeFrame[]
2121
{
22-
RenderTreeFrame.Element(0, string.Empty).WithComponentSubtreeLength(1)
22+
GetEmptyFrameContainer()
2323
};
2424

25+
static RenderTreeFrame GetEmptyFrameContainer()
26+
{
27+
var result = RenderTreeFrame.Element(0, string.Empty);
28+
result.ComponentSubtreeLength = 1;
29+
return result;
30+
}
31+
2532
private static readonly ParameterView _empty = new ParameterView(ParameterViewLifetime.Unbound, _emptyFrames, 0, Array.Empty<CascadingParameterState>());
2633

2734
private readonly ParameterViewLifetime _lifetime;
@@ -216,8 +223,8 @@ internal void CaptureSnapshot(ArrayBuilder<RenderTreeFrame> builder)
216223
public static ParameterView FromDictionary(IDictionary<string, object> parameters)
217224
{
218225
var frames = new RenderTreeFrame[parameters.Count + 1];
219-
frames[0] = RenderTreeFrame.Element(0, GeneratedParameterViewElementName)
220-
.WithElementSubtreeLength(frames.Length);
226+
frames[0] = RenderTreeFrame.Element(0, GeneratedParameterViewElementName);
227+
frames[0].ElementSubtreeLength = frames.Length;
221228

222229
var i = 0;
223230
foreach (var kvp in parameters)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.AspNetCore.Components.RenderTree
5+
{
6+
// Special subclass of ArrayBuilder<T> that contains methods optimized for
7+
// appending render tree frames
8+
9+
internal class RenderTreeArrayBuilder : ArrayBuilder<RenderTreeFrame>
10+
{
11+
public void AppendElement(int sequence, string elementName)
12+
{
13+
if (_itemsInUse == _items.Length)
14+
{
15+
GrowBuffer(_items.Length * 2);
16+
}
17+
18+
ref var item = ref _items[_itemsInUse++];
19+
20+
item.Sequence = sequence;
21+
item.FrameType = RenderTreeFrameType.Element;
22+
item.ElementName = elementName;
23+
}
24+
25+
public void AppendText(int sequence, string text)
26+
{
27+
if (_itemsInUse == _items.Length)
28+
{
29+
GrowBuffer(_items.Length * 2);
30+
}
31+
32+
ref var item = ref _items[_itemsInUse++];
33+
34+
item.Sequence = sequence;
35+
item.FrameType = RenderTreeFrameType.Text;
36+
item.TextContent = text;
37+
}
38+
39+
public void AppendRegion(int sequence)
40+
{
41+
if (_itemsInUse == _items.Length)
42+
{
43+
GrowBuffer(_items.Length * 2);
44+
}
45+
46+
ref var item = ref _items[_itemsInUse++];
47+
48+
item.Sequence = sequence;
49+
item.FrameType = RenderTreeFrameType.Region;
50+
}
51+
}
52+
}

src/Components/Components/src/RenderTree/RenderTreeFrame.cs

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
2222
//
2323
// Represents an entry in a tree of user interface (UI) items.
2424
[StructLayout(LayoutKind.Explicit, Pack = 4)]
25-
public readonly struct RenderTreeFrame
25+
public struct RenderTreeFrame
2626
{
2727
// Note that the struct layout has to be valid in both 32-bit and 64-bit runtime platforms,
2828
// which means that all reference-type fields need to take up 8 bytes (except for the last
@@ -53,12 +53,12 @@ public readonly struct RenderTreeFrame
5353
/// positions of the instructions that inserted the frames. Sequence numbers are only
5454
/// comparable within the same sequence (typically, the same source method).
5555
/// </summary>
56-
[FieldOffset(0)] public readonly int Sequence;
56+
[FieldOffset(0)] public int Sequence;
5757

5858
/// <summary>
5959
/// Describes the type of this frame.
6060
/// </summary>
61-
[FieldOffset(4)] public readonly RenderTreeFrameType FrameType;
61+
[FieldOffset(4)] public RenderTreeFrameType FrameType;
6262

6363
// --------------------------------------------------------------------------------
6464
// RenderTreeFrameType.Element
@@ -69,19 +69,19 @@ public readonly struct RenderTreeFrame
6969
/// gets the number of frames in the subtree for which this frame is the root.
7070
/// The value is zero if the frame has not yet been closed.
7171
/// </summary>
72-
[FieldOffset(8)] public readonly int ElementSubtreeLength;
72+
[FieldOffset(8)] public int ElementSubtreeLength;
7373

7474
/// <summary>
7575
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>,
7676
/// gets a name representing the type of the element. Otherwise, the value is undefined.
7777
/// </summary>
78-
[FieldOffset(16)] public readonly string ElementName;
78+
[FieldOffset(16)] public string ElementName;
7979

8080
/// <summary>
8181
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>,
8282
/// gets the element's diffing key, or null if none was specified.
8383
/// </summary>
84-
[FieldOffset(24)] public readonly object ElementKey;
84+
[FieldOffset(24)] public object ElementKey;
8585

8686
// --------------------------------------------------------------------------------
8787
// RenderTreeFrameType.Text
@@ -91,7 +91,7 @@ public readonly struct RenderTreeFrame
9191
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Text"/>,
9292
/// gets the content of the text frame. Otherwise, the value is undefined.
9393
/// </summary>
94-
[FieldOffset(16)] public readonly string TextContent;
94+
[FieldOffset(16)] public string TextContent;
9595

9696
// --------------------------------------------------------------------------------
9797
// RenderTreeFrameType.Attribute
@@ -101,27 +101,27 @@ public readonly struct RenderTreeFrame
101101
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>
102102
/// gets the ID of the corresponding event handler, if any.
103103
/// </summary>
104-
[FieldOffset(8)] public readonly ulong AttributeEventHandlerId;
104+
[FieldOffset(8)] public ulong AttributeEventHandlerId;
105105

106106
/// <summary>
107107
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
108108
/// gets the attribute name. Otherwise, the value is undefined.
109109
/// </summary>
110-
[FieldOffset(16)] public readonly string AttributeName;
110+
[FieldOffset(16)] public string AttributeName;
111111

112112
/// <summary>
113113
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
114114
/// gets the attribute value. Otherwise, the value is undefined.
115115
/// </summary>
116-
[FieldOffset(24)] public readonly object AttributeValue;
116+
[FieldOffset(24)] public object AttributeValue;
117117

118118
/// <summary>
119119
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
120120
/// and the attribute represents an event handler, gets the name of another attribute whose value
121121
/// can be updated to represent the UI state prior to executing the event handler. This is
122122
/// primarily used in two-way bindings.
123123
/// </summary>
124-
[FieldOffset(32)] public readonly string AttributeEventUpdatesAttributeName;
124+
[FieldOffset(32)] public string AttributeEventUpdatesAttributeName;
125125

126126
// --------------------------------------------------------------------------------
127127
// RenderTreeFrameType.Component
@@ -132,31 +132,31 @@ public readonly struct RenderTreeFrame
132132
/// gets the number of frames in the subtree for which this frame is the root.
133133
/// The value is zero if the frame has not yet been closed.
134134
/// </summary>
135-
[FieldOffset(8)] public readonly int ComponentSubtreeLength;
135+
[FieldOffset(8)] public int ComponentSubtreeLength;
136136

137137
/// <summary>
138138
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
139139
/// gets the child component instance identifier.
140140
/// </summary>
141-
[FieldOffset(12)] public readonly int ComponentId;
141+
[FieldOffset(12)] public int ComponentId;
142142

143143
/// <summary>
144144
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
145145
/// gets the type of the child component.
146146
/// </summary>
147-
[FieldOffset(16)] public readonly Type ComponentType;
147+
[FieldOffset(16)] public Type ComponentType;
148148

149149
/// <summary>
150150
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
151151
/// gets the child component state object. Otherwise, the value is undefined.
152152
/// </summary>
153-
[FieldOffset(24)] internal readonly ComponentState ComponentState;
153+
[FieldOffset(24)] internal ComponentState ComponentState;
154154

155155
/// <summary>
156156
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
157157
/// gets the component's diffing key, or null if none was specified.
158158
/// </summary>
159-
[FieldOffset(32)] public readonly object ComponentKey;
159+
[FieldOffset(32)] public object ComponentKey;
160160

161161
/// <summary>
162162
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
@@ -173,7 +173,7 @@ public readonly struct RenderTreeFrame
173173
/// gets the number of frames in the subtree for which this frame is the root.
174174
/// The value is zero if the frame has not yet been closed.
175175
/// </summary>
176-
[FieldOffset(8)] public readonly int RegionSubtreeLength;
176+
[FieldOffset(8)] public int RegionSubtreeLength;
177177

178178
// --------------------------------------------------------------------------------
179179
// RenderTreeFrameType.ElementReferenceCapture
@@ -183,13 +183,13 @@ public readonly struct RenderTreeFrame
183183
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ElementReferenceCapture"/>,
184184
/// gets the ID of the reference capture. Otherwise, the value is undefined.
185185
/// </summary>
186-
[FieldOffset(16)] public readonly string ElementReferenceCaptureId;
186+
[FieldOffset(16)] public string ElementReferenceCaptureId;
187187

188188
/// <summary>
189189
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ElementReferenceCapture"/>,
190190
/// gets the action that writes the reference to its target. Otherwise, the value is undefined.
191191
/// </summary>
192-
[FieldOffset(24)] public readonly Action<ElementReference> ElementReferenceCaptureAction;
192+
[FieldOffset(24)] public Action<ElementReference> ElementReferenceCaptureAction;
193193

194194
// --------------------------------------------------------------------------------
195195
// RenderTreeFrameType.ComponentReferenceCapture
@@ -205,13 +205,13 @@ public readonly struct RenderTreeFrame
205205
/// initialization logic in RenderTreeDiffBuilder to walk the frames hierarchically, then it would know
206206
/// the parent index at the point where it wants to initialize the ComponentReferenceCapture frame.
207207
/// </summary>
208-
[FieldOffset(8)] public readonly int ComponentReferenceCaptureParentFrameIndex;
208+
[FieldOffset(8)] public int ComponentReferenceCaptureParentFrameIndex;
209209

210210
/// <summary>
211211
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ComponentReferenceCapture"/>,
212212
/// gets the action that writes the reference to its target. Otherwise, the value is undefined.
213213
/// </summary>
214-
[FieldOffset(16)] public readonly Action<object> ComponentReferenceCaptureAction;
214+
[FieldOffset(16)] public Action<object> ComponentReferenceCaptureAction;
215215

216216
// --------------------------------------------------------------------------------
217217
// RenderTreeFrameType.Markup
@@ -221,7 +221,7 @@ public readonly struct RenderTreeFrame
221221
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Markup"/>,
222222
/// gets the content of the markup frame. Otherwise, the value is undefined.
223223
/// </summary>
224-
[FieldOffset(16)] public readonly string MarkupContent;
224+
[FieldOffset(16)] public string MarkupContent;
225225

226226
// Element constructor
227227
private RenderTreeFrame(int sequence, int elementSubtreeLength, string elementName, object elementKey)
@@ -336,12 +336,13 @@ internal static RenderTreeFrame ElementReferenceCapture(int sequence, Action<Ele
336336
internal static RenderTreeFrame ComponentReferenceCapture(int sequence, Action<object> componentReferenceCaptureAction, int parentFrameIndex)
337337
=> new RenderTreeFrame(sequence, componentReferenceCaptureAction: componentReferenceCaptureAction, parentFrameIndex: parentFrameIndex);
338338

339+
/*
339340
internal RenderTreeFrame WithElementSubtreeLength(int elementSubtreeLength)
340341
=> new RenderTreeFrame(Sequence, elementSubtreeLength: elementSubtreeLength, ElementName, ElementKey);
341342
342343
internal RenderTreeFrame WithComponentSubtreeLength(int componentSubtreeLength)
343344
=> new RenderTreeFrame(Sequence, componentSubtreeLength: componentSubtreeLength, ComponentType, ComponentState, ComponentKey);
344-
345+
*/
345346
internal RenderTreeFrame WithAttributeSequence(int sequence)
346347
=> new RenderTreeFrame(sequence, attributeName: AttributeName, AttributeValue, AttributeEventHandlerId, AttributeEventUpdatesAttributeName);
347348

src/Components/Components/src/Rendering/RenderTreeBuilder.cs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public sealed class RenderTreeBuilder : IDisposable
2424
private readonly static object BoxedFalse = false;
2525
private readonly static string ComponentReferenceCaptureInvalidParentMessage = $"Component reference captures may only be added as children of frames of type {RenderTreeFrameType.Component}";
2626

27-
private readonly ArrayBuilder<RenderTreeFrame> _entries = new ArrayBuilder<RenderTreeFrame>();
27+
private readonly RenderTreeArrayBuilder _entries = new RenderTreeArrayBuilder();
2828
private readonly Stack<int> _openElementIndices = new Stack<int>();
2929
private RenderTreeFrameType? _lastNonAttributeFrameType;
3030
private bool _hasSeenAddMultipleAttributes;
@@ -55,7 +55,9 @@ public void OpenElement(int sequence, string elementName)
5555
}
5656

5757
_openElementIndices.Push(_entries.Count);
58-
Append(RenderTreeFrame.Element(sequence, elementName));
58+
_entries.AppendElement(sequence, elementName);
59+
_lastNonAttributeFrameType = RenderTreeFrameType.Element;
60+
//Append(RenderTreeFrame.Element(sequence, elementName));
5961
ProfilingEnd();
6062
}
6163

@@ -76,7 +78,7 @@ public void CloseElement()
7678
}
7779

7880
ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed];
79-
entry = entry.WithElementSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
81+
entry.ElementSubtreeLength = _entries.Count - indexOfEntryBeingClosed;
8082
ProfilingEnd();
8183
}
8284

@@ -100,7 +102,9 @@ public void AddMarkupContent(int sequence, string? markupContent)
100102
public void AddContent(int sequence, string? textContent)
101103
{
102104
ProfilingStart();
103-
Append(RenderTreeFrame.Text(sequence, textContent ?? string.Empty));
105+
_entries.AppendText(sequence, textContent ?? string.Empty);
106+
_lastNonAttributeFrameType = RenderTreeFrameType.Text;
107+
//Append(RenderTreeFrame.Text(sequence, textContent ?? string.Empty));
104108
ProfilingEnd();
105109
}
106110

@@ -149,6 +153,14 @@ public void AddContent<TValue>(int sequence, RenderFragment<TValue>? fragment, T
149153
public void AddContent(int sequence, MarkupString markupContent)
150154
=> AddMarkupContent(sequence, markupContent.Value);
151155

156+
/// <summary>
157+
/// Appends a frame representing numeric content.
158+
/// </summary>
159+
/// <param name="sequence">An integer that represents the position of the instruction in the source code.</param>
160+
/// <param name="content">Content for the new text frame.</param>
161+
public void AddContent(int sequence, int content)
162+
=> AddContent(sequence, content.ToString());
163+
152164
/// <summary>
153165
/// Appends a frame representing text content.
154166
/// </summary>
@@ -599,7 +611,7 @@ public void CloseComponent()
599611
}
600612

601613
ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed];
602-
entry = entry.WithComponentSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
614+
entry.ComponentSubtreeLength = _entries.Count - indexOfEntryBeingClosed;
603615
ProfilingEnd();
604616
}
605617

@@ -660,7 +672,9 @@ public void OpenRegion(int sequence)
660672
}
661673

662674
_openElementIndices.Push(_entries.Count);
663-
Append(RenderTreeFrame.Region(sequence));
675+
_entries.AppendRegion(sequence);
676+
_lastNonAttributeFrameType = RenderTreeFrameType.Region;
677+
//Append(RenderTreeFrame.Region(sequence));
664678
ProfilingEnd();
665679
}
666680

@@ -673,7 +687,7 @@ public void CloseRegion()
673687
ProfilingStart();
674688
var indexOfEntryBeingClosed = _openElementIndices.Pop();
675689
ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed];
676-
entry = entry.WithRegionSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
690+
entry.RegionSubtreeLength = _entries.Count - indexOfEntryBeingClosed;
677691
ProfilingEnd();
678692
}
679693

src/Components/Components/src/Rendering/RenderTreeUpdater.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ private static void UpdateFrameToMatchClientState(RenderTreeBuilder renderTreeBu
9292
var otherFrameEndIndexExcl = otherFrameIndex + otherFrameSubtreeLength;
9393
if (otherFrameEndIndexExcl > elementFrameIndex) // i.e., contains the element we're inserting into
9494
{
95-
otherFrame = otherFrame.WithElementSubtreeLength(otherFrameSubtreeLength + 1);
95+
otherFrame.ElementSubtreeLength = otherFrameSubtreeLength + 1;
9696
}
9797
break;
9898
}

0 commit comments

Comments
 (0)