Remove Blazor internal profiling infrastructure (#24468)

* Put back InternalCalls

* Removing .NET profiling calls

* Remove JS side profiling
This commit is contained in:
Steve Sanderson 2020-07-31 22:04:48 +01:00 committed by GitHub
parent a64f3fda00
commit 6f7a3dfd4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 5 additions and 377 deletions

View File

@ -10,7 +10,6 @@
<ItemGroup> <ItemGroup>
<Compile Include="$(ComponentsSharedSourceRoot)src\ArrayBuilder.cs" LinkBase="RenderTree" /> <Compile Include="$(ComponentsSharedSourceRoot)src\ArrayBuilder.cs" LinkBase="RenderTree" />
<Compile Include="$(ComponentsSharedSourceRoot)src\WebAssemblyJSInteropInternalCalls.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,23 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Microsoft.AspNetCore.Components.Profiling
{
internal abstract class ComponentsProfiling
{
// For now, this is only intended for use on Blazor WebAssembly, and will have no effect
// when running on Blazor Server. The reason for having the ComponentsProfiling abstraction
// is so that if we later have two different implementations (one for WebAssembly, one for
// Server), the execution characteristics of calling Start/End will be unchanged and historical
// perf data will still be comparable to newer data.
public static readonly ComponentsProfiling Instance = PlatformInfo.IsWebAssembly
? new WebAssemblyComponentsProfiling()
: (ComponentsProfiling)new NoOpComponentsProfiling();
public abstract void Start([CallerMemberName] string? name = null);
public abstract void End([CallerMemberName] string? name = null);
}
}

View File

@ -1,16 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Components.Profiling
{
internal class NoOpComponentsProfiling : ComponentsProfiling
{
public override void Start(string? name)
{
}
public override void End(string? name)
{
}
}
}

View File

@ -1,41 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using WebAssembly.JSInterop;
namespace Microsoft.AspNetCore.Components.Profiling
{
// Later on, we will likely want to move this into the WebAssembly package. However it needs to
// be inlined into the Components package directly until we're ready to make the underlying
// ComponentsProfile abstraction into a public API. It's possible that this API will never become
// public, or that it will be replaced by something more standard for .NET, if it's possible to
// make that work performantly on WebAssembly.
internal class WebAssemblyComponentsProfiling : ComponentsProfiling
{
static bool IsCapturing = false;
public static void SetCapturing(bool isCapturing)
{
IsCapturing = isCapturing;
}
public override void Start(string? name)
{
if (IsCapturing)
{
InternalCalls.InvokeJSUnmarshalled<string, object, object, object>(
out _, "_blazorProfileStart", name, null, null);
}
}
public override void End(string? name)
{
if (IsCapturing)
{
InternalCalls.InvokeJSUnmarshalled<string, object, object, object>(
out _, "_blazorProfileEnd", name, null, null);
}
}
}
}

View File

@ -7,7 +7,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Components.Profiling;
using Microsoft.AspNetCore.Components.Rendering; using Microsoft.AspNetCore.Components.Rendering;
namespace Microsoft.AspNetCore.Components.RenderTree namespace Microsoft.AspNetCore.Components.RenderTree
@ -28,7 +27,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
ArrayRange<RenderTreeFrame> oldTree, ArrayRange<RenderTreeFrame> oldTree,
ArrayRange<RenderTreeFrame> newTree) ArrayRange<RenderTreeFrame> newTree)
{ {
ComponentsProfiling.Instance.Start();
var editsBuffer = batchBuilder.EditsBuffer; var editsBuffer = batchBuilder.EditsBuffer;
var editsBufferStartLength = editsBuffer.Count; var editsBufferStartLength = editsBuffer.Count;
@ -37,7 +35,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
var editsSegment = editsBuffer.ToSegment(editsBufferStartLength, editsBuffer.Count); var editsSegment = editsBuffer.ToSegment(editsBufferStartLength, editsBuffer.Count);
var result = new RenderTreeDiff(componentId, editsSegment); var result = new RenderTreeDiff(componentId, editsSegment);
ComponentsProfiling.Instance.End();
return result; return result;
} }
@ -49,7 +46,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
int oldStartIndex, int oldEndIndexExcl, int oldStartIndex, int oldEndIndexExcl,
int newStartIndex, int newEndIndexExcl) int newStartIndex, int newEndIndexExcl)
{ {
ProfilingStart();
// This is deliberately a very large method. Parts of it could be factored out // This is deliberately a very large method. Parts of it could be factored out
// into other private methods, but doing so comes at a consequential perf cost, // into other private methods, but doing so comes at a consequential perf cost,
// because it involves so much parameter passing. You can think of the code here // because it involves so much parameter passing. You can think of the code here
@ -300,12 +296,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree
diffContext.KeyedItemInfoDictionaryPool.Return(keyedItemInfos); diffContext.KeyedItemInfoDictionaryPool.Return(keyedItemInfos);
} }
} }
ProfilingEnd();
} }
private static Dictionary<object, KeyedItemInfo> BuildKeyToInfoLookup(DiffContext diffContext, int oldStartIndex, int oldEndIndexExcl, int newStartIndex, int newEndIndexExcl) private static Dictionary<object, KeyedItemInfo> BuildKeyToInfoLookup(DiffContext diffContext, int oldStartIndex, int oldEndIndexExcl, int newStartIndex, int newEndIndexExcl)
{ {
ProfilingStart();
var result = diffContext.KeyedItemInfoDictionaryPool.Get(); var result = diffContext.KeyedItemInfoDictionaryPool.Get();
var oldTree = diffContext.OldTree; var oldTree = diffContext.OldTree;
var newTree = diffContext.NewTree; var newTree = diffContext.NewTree;
@ -351,7 +345,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
newStartIndex = NextSiblingIndex(frame, newStartIndex); newStartIndex = NextSiblingIndex(frame, newStartIndex);
} }
ProfilingEnd();
return result; return result;
} }
@ -394,7 +387,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
int oldStartIndex, int oldEndIndexExcl, int oldStartIndex, int oldEndIndexExcl,
int newStartIndex, int newEndIndexExcl) int newStartIndex, int newEndIndexExcl)
{ {
ProfilingStart();
// The overhead of the dictionary used by AppendAttributeDiffEntriesForRangeSlow is // The overhead of the dictionary used by AppendAttributeDiffEntriesForRangeSlow is
// significant, so we want to try and do a merge-join if possible, but fall back to // significant, so we want to try and do a merge-join if possible, but fall back to
// a hash-join if not. We'll do a merge join until we hit a case we can't handle and // a hash-join if not. We'll do a merge join until we hit a case we can't handle and
@ -443,7 +435,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
ref diffContext, ref diffContext,
oldStartIndex, oldEndIndexExcl, oldStartIndex, oldEndIndexExcl,
newStartIndex, newEndIndexExcl); newStartIndex, newEndIndexExcl);
ProfilingEnd();
return; return;
} }
@ -469,12 +460,9 @@ namespace Microsoft.AspNetCore.Components.RenderTree
ref diffContext, ref diffContext,
oldStartIndex, oldEndIndexExcl, oldStartIndex, oldEndIndexExcl,
newStartIndex, newEndIndexExcl); newStartIndex, newEndIndexExcl);
ProfilingEnd();
return; return;
} }
} }
ProfilingEnd();
} }
private static void AppendAttributeDiffEntriesForRangeSlow( private static void AppendAttributeDiffEntriesForRangeSlow(
@ -482,7 +470,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
int oldStartIndex, int oldEndIndexExcl, int oldStartIndex, int oldEndIndexExcl,
int newStartIndex, int newEndIndexExcl) int newStartIndex, int newEndIndexExcl)
{ {
ProfilingStart();
var oldTree = diffContext.OldTree; var oldTree = diffContext.OldTree;
var newTree = diffContext.NewTree; var newTree = diffContext.NewTree;
@ -521,7 +508,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
// We should have processed any additions at this point. Reset for the next batch. // We should have processed any additions at this point. Reset for the next batch.
diffContext.AttributeDiffSet.Clear(); diffContext.AttributeDiffSet.Clear();
ProfilingEnd();
} }
private static void UpdateRetainedChildComponent( private static void UpdateRetainedChildComponent(
@ -529,7 +515,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
int oldComponentIndex, int oldComponentIndex,
int newComponentIndex) int newComponentIndex)
{ {
ProfilingStart();
var oldTree = diffContext.OldTree; var oldTree = diffContext.OldTree;
var newTree = diffContext.NewTree; var newTree = diffContext.NewTree;
ref var oldComponentFrame = ref oldTree[oldComponentIndex]; ref var oldComponentFrame = ref oldTree[oldComponentIndex];
@ -556,8 +541,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
{ {
componentState.SetDirectParameters(newParameters); componentState.SetDirectParameters(newParameters);
} }
ProfilingEnd();
} }
private static int NextSiblingIndex(in RenderTreeFrame frame, int frameIndex) private static int NextSiblingIndex(in RenderTreeFrame frame, int frameIndex)
@ -580,7 +563,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
int oldFrameIndex, int oldFrameIndex,
int newFrameIndex) int newFrameIndex)
{ {
ProfilingStart();
var oldTree = diffContext.OldTree; var oldTree = diffContext.OldTree;
var newTree = diffContext.NewTree; var newTree = diffContext.NewTree;
ref var oldFrame = ref oldTree[oldFrameIndex]; ref var oldFrame = ref oldTree[oldFrameIndex];
@ -593,7 +575,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
{ {
InsertNewFrame(ref diffContext, newFrameIndex); InsertNewFrame(ref diffContext, newFrameIndex);
RemoveOldFrame(ref diffContext, oldFrameIndex); RemoveOldFrame(ref diffContext, oldFrameIndex);
ProfilingEnd();
return; return;
} }
@ -719,8 +700,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
default: default:
throw new NotImplementedException($"Encountered unsupported frame type during diffing: {newTree[newFrameIndex].FrameType}"); throw new NotImplementedException($"Encountered unsupported frame type during diffing: {newTree[newFrameIndex].FrameType}");
} }
ProfilingEnd();
} }
// This should only be called for attributes that have the same name. This is an // This should only be called for attributes that have the same name. This is an
@ -730,7 +709,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
int oldFrameIndex, int oldFrameIndex,
int newFrameIndex) int newFrameIndex)
{ {
ProfilingStart();
var oldTree = diffContext.OldTree; var oldTree = diffContext.OldTree;
var newTree = diffContext.NewTree; var newTree = diffContext.NewTree;
ref var oldFrame = ref oldTree[oldFrameIndex]; ref var oldFrame = ref oldTree[oldFrameIndex];
@ -759,13 +737,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree
// since it was unchanged. // since it was unchanged.
newFrame = oldFrame; newFrame = oldFrame;
} }
ProfilingEnd();
} }
private static void InsertNewFrame(ref DiffContext diffContext, int newFrameIndex) private static void InsertNewFrame(ref DiffContext diffContext, int newFrameIndex)
{ {
ProfilingStart();
var newTree = diffContext.NewTree; var newTree = diffContext.NewTree;
ref var newFrame = ref newTree[newFrameIndex]; ref var newFrame = ref newTree[newFrameIndex];
switch (newFrame.FrameType) switch (newFrame.FrameType)
@ -818,12 +793,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree
default: default:
throw new NotImplementedException($"Unexpected frame type during {nameof(InsertNewFrame)}: {newFrame.FrameType}"); throw new NotImplementedException($"Unexpected frame type during {nameof(InsertNewFrame)}: {newFrame.FrameType}");
} }
ProfilingEnd();
} }
private static void RemoveOldFrame(ref DiffContext diffContext, int oldFrameIndex) private static void RemoveOldFrame(ref DiffContext diffContext, int oldFrameIndex)
{ {
ProfilingStart();
var oldTree = diffContext.OldTree; var oldTree = diffContext.OldTree;
ref var oldFrame = ref oldTree[oldFrameIndex]; ref var oldFrame = ref oldTree[oldFrameIndex];
switch (oldFrame.FrameType) switch (oldFrame.FrameType)
@ -865,7 +838,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
default: default:
throw new NotImplementedException($"Unexpected frame type during {nameof(RemoveOldFrame)}: {oldFrame.FrameType}"); throw new NotImplementedException($"Unexpected frame type during {nameof(RemoveOldFrame)}: {oldFrame.FrameType}");
} }
ProfilingEnd();
} }
private static int GetAttributesEndIndexExclusive(RenderTreeFrame[] tree, int rootIndex) private static int GetAttributesEndIndexExclusive(RenderTreeFrame[] tree, int rootIndex)
@ -899,7 +871,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
private static void InitializeNewSubtree(ref DiffContext diffContext, int frameIndex) private static void InitializeNewSubtree(ref DiffContext diffContext, int frameIndex)
{ {
ProfilingStart();
var frames = diffContext.NewTree; var frames = diffContext.NewTree;
var endIndexExcl = frameIndex + frames[frameIndex].ElementSubtreeLength; var endIndexExcl = frameIndex + frames[frameIndex].ElementSubtreeLength;
for (var i = frameIndex; i < endIndexExcl; i++) for (var i = frameIndex; i < endIndexExcl; i++)
@ -921,12 +892,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree
break; break;
} }
} }
ProfilingEnd();
} }
private static void InitializeNewComponentFrame(ref DiffContext diffContext, int frameIndex) private static void InitializeNewComponentFrame(ref DiffContext diffContext, int frameIndex)
{ {
ProfilingStart();
var frames = diffContext.NewTree; var frames = diffContext.NewTree;
ref var frame = ref frames[frameIndex]; ref var frame = ref frames[frameIndex];
@ -943,7 +912,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
var initialParametersLifetime = new ParameterViewLifetime(diffContext.BatchBuilder); var initialParametersLifetime = new ParameterViewLifetime(diffContext.BatchBuilder);
var initialParameters = new ParameterView(initialParametersLifetime, frames, frameIndex); var initialParameters = new ParameterView(initialParametersLifetime, frames, frameIndex);
childComponentState.SetDirectParameters(initialParameters); childComponentState.SetDirectParameters(initialParameters);
ProfilingEnd();
} }
private static void InitializeNewAttributeFrame(ref DiffContext diffContext, ref RenderTreeFrame newFrame) private static void InitializeNewAttributeFrame(ref DiffContext diffContext, ref RenderTreeFrame newFrame)
@ -988,7 +956,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
private static void DisposeFramesInRange(RenderBatchBuilder batchBuilder, RenderTreeFrame[] frames, int startIndex, int endIndexExcl) private static void DisposeFramesInRange(RenderBatchBuilder batchBuilder, RenderTreeFrame[] frames, int startIndex, int endIndexExcl)
{ {
ProfilingStart();
for (var i = startIndex; i < endIndexExcl; i++) for (var i = startIndex; i < endIndexExcl; i++)
{ {
ref var frame = ref frames[i]; ref var frame = ref frames[i];
@ -1001,7 +968,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
batchBuilder.DisposedEventHandlerIds.Append(frame.AttributeEventHandlerId); batchBuilder.DisposedEventHandlerIds.Append(frame.AttributeEventHandlerId);
} }
} }
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -1043,18 +1009,5 @@ namespace Microsoft.AspNetCore.Components.RenderTree
SiblingIndex = 0; SiblingIndex = 0;
} }
} }
// Having too many calls to ComponentsProfiling.Instance.Start/End has a measurable perf impact
// even when capturing is disabled. So, to enable detailed profiling for this class, define the
// Profile_RenderTreeDiffBuilder compiler symbol, otherwise the calls are compiled out entirely.
// Enabling detailed profiling adds about 5% to rendering benchmark times.
[Conditional("Profile_RenderTreeDiffBuilder")]
private static void ProfilingStart([CallerMemberName] string? name = null)
=> ComponentsProfiling.Instance.Start(name);
[Conditional("Profile_RenderTreeDiffBuilder")]
private static void ProfilingEnd([CallerMemberName] string? name = null)
=> ComponentsProfiling.Instance.End(name);
} }
} }

View File

@ -8,7 +8,6 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Profiling;
using Microsoft.AspNetCore.Components.Rendering; using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -247,7 +246,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
/// </returns> /// </returns>
public virtual Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo fieldInfo, EventArgs eventArgs) public virtual Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo fieldInfo, EventArgs eventArgs)
{ {
ComponentsProfiling.Instance.Start();
Dispatcher.AssertAccess(); Dispatcher.AssertAccess();
if (!_eventBindings.TryGetValue(eventHandlerId, out var callback)) if (!_eventBindings.TryGetValue(eventHandlerId, out var callback))
@ -275,7 +273,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
catch (Exception e) catch (Exception e)
{ {
HandleException(e); HandleException(e);
ComponentsProfiling.Instance.End();
return Task.CompletedTask; return Task.CompletedTask;
} }
finally finally
@ -290,7 +287,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
// Task completed synchronously or is still running. We already processed all of the rendering // Task completed synchronously or is still running. We already processed all of the rendering
// work that was queued so let our error handler deal with it. // work that was queued so let our error handler deal with it.
var result = GetErrorHandledTask(task); var result = GetErrorHandledTask(task);
ComponentsProfiling.Instance.End();
return result; return result;
} }
@ -441,7 +437,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
private void ProcessRenderQueue() private void ProcessRenderQueue()
{ {
ComponentsProfiling.Instance.Start();
Dispatcher.AssertAccess(); Dispatcher.AssertAccess();
if (_isBatchInProgress) if (_isBatchInProgress)
@ -456,7 +451,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
{ {
if (_batchBuilder.ComponentRenderQueue.Count == 0) if (_batchBuilder.ComponentRenderQueue.Count == 0)
{ {
ComponentsProfiling.Instance.End();
return; return;
} }
@ -468,9 +462,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
} }
var batch = _batchBuilder.ToBatch(); var batch = _batchBuilder.ToBatch();
ComponentsProfiling.Instance.Start(nameof(UpdateDisplayAsync));
updateDisplayTask = UpdateDisplayAsync(batch); updateDisplayTask = UpdateDisplayAsync(batch);
ComponentsProfiling.Instance.End(nameof(UpdateDisplayAsync));
// Fire off the execution of OnAfterRenderAsync, but don't wait for it // Fire off the execution of OnAfterRenderAsync, but don't wait for it
// if there is async work to be done. // if there is async work to be done.
@ -480,7 +472,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
{ {
// Ensure we catch errors while running the render functions of the components. // Ensure we catch errors while running the render functions of the components.
HandleException(e); HandleException(e);
ComponentsProfiling.Instance.End();
return; return;
} }
finally finally
@ -498,8 +489,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
{ {
ProcessRenderQueue(); ProcessRenderQueue();
} }
ComponentsProfiling.Instance.End();
} }
private Task InvokeRenderCompletedCalls(ArrayRange<RenderTreeDiff> updatedComponents, Task updateDisplayTask) private Task InvokeRenderCompletedCalls(ArrayRange<RenderTreeDiff> updatedComponents, Task updateDisplayTask)

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Profiling;
using Microsoft.AspNetCore.Components.RenderTree; using Microsoft.AspNetCore.Components.RenderTree;
namespace Microsoft.AspNetCore.Components.Rendering namespace Microsoft.AspNetCore.Components.Rendering
@ -57,7 +56,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
public void RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment) public void RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment)
{ {
ComponentsProfiling.Instance.Start();
// A component might be in the render queue already before getting disposed by an // A component might be in the render queue already before getting disposed by an
// earlier entry in the render queue. In that case, rendering is a no-op. // earlier entry in the render queue. In that case, rendering is a no-op.
if (_componentWasDisposed) if (_componentWasDisposed)
@ -69,9 +67,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
(CurrentRenderTree, _renderTreeBuilderPrevious) = (_renderTreeBuilderPrevious, CurrentRenderTree); (CurrentRenderTree, _renderTreeBuilderPrevious) = (_renderTreeBuilderPrevious, CurrentRenderTree);
CurrentRenderTree.Clear(); CurrentRenderTree.Clear();
ComponentsProfiling.Instance.Start("BuildRenderTree");
renderFragment(CurrentRenderTree); renderFragment(CurrentRenderTree);
ComponentsProfiling.Instance.End("BuildRenderTree");
var diff = RenderTreeDiffBuilder.ComputeDiff( var diff = RenderTreeDiffBuilder.ComputeDiff(
_renderer, _renderer,
@ -81,7 +77,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
CurrentRenderTree.GetFrames()); CurrentRenderTree.GetFrames());
batchBuilder.UpdatedComponentDiffs.Append(diff); batchBuilder.UpdatedComponentDiffs.Append(diff);
batchBuilder.InvalidateParameterViews(); batchBuilder.InvalidateParameterViews();
ComponentsProfiling.Instance.End();
} }
public bool TryDisposeInBatch(RenderBatchBuilder batchBuilder, [NotNullWhen(false)] out Exception? exception) public bool TryDisposeInBatch(RenderBatchBuilder batchBuilder, [NotNullWhen(false)] out Exception? exception)

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Components.Profiling;
using Microsoft.AspNetCore.Components.RenderTree; using Microsoft.AspNetCore.Components.RenderTree;
namespace Microsoft.AspNetCore.Components.Rendering namespace Microsoft.AspNetCore.Components.Rendering
@ -45,7 +44,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="elementName">A value representing the type of the element.</param> /// <param name="elementName">A value representing the type of the element.</param>
public void OpenElement(int sequence, string elementName) public void OpenElement(int sequence, string elementName)
{ {
ProfilingStart();
// We are entering a new scope, since we track the "duplicate attributes" per // We are entering a new scope, since we track the "duplicate attributes" per
// element/component we might need to clean them up now. // element/component we might need to clean them up now.
if (_hasSeenAddMultipleAttributes) if (_hasSeenAddMultipleAttributes)
@ -56,7 +54,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
_openElementIndices.Push(_entries.Count); _openElementIndices.Push(_entries.Count);
Append(RenderTreeFrame.Element(sequence, elementName)); Append(RenderTreeFrame.Element(sequence, elementName));
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -65,7 +62,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// </summary> /// </summary>
public void CloseElement() public void CloseElement()
{ {
ProfilingStart();
var indexOfEntryBeingClosed = _openElementIndices.Pop(); var indexOfEntryBeingClosed = _openElementIndices.Pop();
// We might be closing an element with only attributes, run the duplicate cleanup pass // We might be closing an element with only attributes, run the duplicate cleanup pass
@ -77,7 +73,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed]; ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed];
entry = entry.WithElementSubtreeLength(_entries.Count - indexOfEntryBeingClosed); entry = entry.WithElementSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -87,9 +82,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="markupContent">Content for the new markup frame.</param> /// <param name="markupContent">Content for the new markup frame.</param>
public void AddMarkupContent(int sequence, string? markupContent) public void AddMarkupContent(int sequence, string? markupContent)
{ {
ProfilingStart();
Append(RenderTreeFrame.Markup(sequence, markupContent ?? string.Empty)); Append(RenderTreeFrame.Markup(sequence, markupContent ?? string.Empty));
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -99,9 +92,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="textContent">Content for the new text frame.</param> /// <param name="textContent">Content for the new text frame.</param>
public void AddContent(int sequence, string? textContent) public void AddContent(int sequence, string? textContent)
{ {
ProfilingStart();
Append(RenderTreeFrame.Text(sequence, textContent ?? string.Empty)); Append(RenderTreeFrame.Text(sequence, textContent ?? string.Empty));
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -111,7 +102,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="fragment">Content to append.</param> /// <param name="fragment">Content to append.</param>
public void AddContent(int sequence, RenderFragment? fragment) public void AddContent(int sequence, RenderFragment? fragment)
{ {
ProfilingStart();
if (fragment != null) if (fragment != null)
{ {
// We surround the fragment with a region delimiter to indicate that the // We surround the fragment with a region delimiter to indicate that the
@ -122,7 +112,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
fragment(this); fragment(this);
CloseRegion(); CloseRegion();
} }
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -133,12 +122,10 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="value">The value used by <paramref name="fragment"/>.</param> /// <param name="value">The value used by <paramref name="fragment"/>.</param>
public void AddContent<TValue>(int sequence, RenderFragment<TValue>? fragment, TValue value) public void AddContent<TValue>(int sequence, RenderFragment<TValue>? fragment, TValue value)
{ {
ProfilingStart();
if (fragment != null) if (fragment != null)
{ {
AddContent(sequence, fragment(value)); AddContent(sequence, fragment(value));
} }
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -169,15 +156,12 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="name">The name of the attribute.</param> /// <param name="name">The name of the attribute.</param>
public void AddAttribute(int sequence, string name) public void AddAttribute(int sequence, string name)
{ {
ProfilingStart();
if (_lastNonAttributeFrameType != RenderTreeFrameType.Element) if (_lastNonAttributeFrameType != RenderTreeFrameType.Element)
{ {
throw new InvalidOperationException($"Valueless attributes may only be added immediately after frames of type {RenderTreeFrameType.Element}"); throw new InvalidOperationException($"Valueless attributes may only be added immediately after frames of type {RenderTreeFrameType.Element}");
} }
Append(RenderTreeFrame.Attribute(sequence, name, BoxedTrue)); Append(RenderTreeFrame.Attribute(sequence, name, BoxedTrue));
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -194,7 +178,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="value">The value of the attribute.</param> /// <param name="value">The value of the attribute.</param>
public void AddAttribute(int sequence, string name, bool value) public void AddAttribute(int sequence, string name, bool value)
{ {
ProfilingStart();
AssertCanAddAttribute(); AssertCanAddAttribute();
if (_lastNonAttributeFrameType == RenderTreeFrameType.Component) if (_lastNonAttributeFrameType == RenderTreeFrameType.Component)
{ {
@ -210,7 +193,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
{ {
TrackAttributeName(name); TrackAttributeName(name);
} }
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -227,7 +209,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="value">The value of the attribute.</param> /// <param name="value">The value of the attribute.</param>
public void AddAttribute(int sequence, string name, string? value) public void AddAttribute(int sequence, string name, string? value)
{ {
ProfilingStart();
AssertCanAddAttribute(); AssertCanAddAttribute();
if (value != null || _lastNonAttributeFrameType == RenderTreeFrameType.Component) if (value != null || _lastNonAttributeFrameType == RenderTreeFrameType.Component)
{ {
@ -237,7 +218,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
{ {
TrackAttributeName(name); TrackAttributeName(name);
} }
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -254,7 +234,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="value">The value of the attribute.</param> /// <param name="value">The value of the attribute.</param>
public void AddAttribute(int sequence, string name, MulticastDelegate? value) public void AddAttribute(int sequence, string name, MulticastDelegate? value)
{ {
ProfilingStart();
AssertCanAddAttribute(); AssertCanAddAttribute();
if (value != null || _lastNonAttributeFrameType == RenderTreeFrameType.Component) if (value != null || _lastNonAttributeFrameType == RenderTreeFrameType.Component)
{ {
@ -264,7 +243,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
{ {
TrackAttributeName(name); TrackAttributeName(name);
} }
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -285,7 +263,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// </remarks> /// </remarks>
public void AddAttribute(int sequence, string name, EventCallback value) public void AddAttribute(int sequence, string name, EventCallback value)
{ {
ProfilingStart();
AssertCanAddAttribute(); AssertCanAddAttribute();
if (_lastNonAttributeFrameType == RenderTreeFrameType.Component) if (_lastNonAttributeFrameType == RenderTreeFrameType.Component)
{ {
@ -310,7 +287,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
// Track the attribute name if needed since we elided the frame. // Track the attribute name if needed since we elided the frame.
TrackAttributeName(name); TrackAttributeName(name);
} }
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -331,7 +307,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// </remarks> /// </remarks>
public void AddAttribute<TArgument>(int sequence, string name, EventCallback<TArgument> value) public void AddAttribute<TArgument>(int sequence, string name, EventCallback<TArgument> value)
{ {
ProfilingStart();
AssertCanAddAttribute(); AssertCanAddAttribute();
if (_lastNonAttributeFrameType == RenderTreeFrameType.Component) if (_lastNonAttributeFrameType == RenderTreeFrameType.Component)
{ {
@ -356,7 +331,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
// Track the attribute name if needed since we elided the frame. // Track the attribute name if needed since we elided the frame.
TrackAttributeName(name); TrackAttributeName(name);
} }
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -370,7 +344,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="value">The value of the attribute.</param> /// <param name="value">The value of the attribute.</param>
public void AddAttribute(int sequence, string name, object? value) public void AddAttribute(int sequence, string name, object? value)
{ {
ProfilingStart();
// This looks a bit daunting because we need to handle the boxed/object version of all of the // This looks a bit daunting because we need to handle the boxed/object version of all of the
// types that AddAttribute special cases. // types that AddAttribute special cases.
if (_lastNonAttributeFrameType == RenderTreeFrameType.Element) if (_lastNonAttributeFrameType == RenderTreeFrameType.Element)
@ -423,7 +396,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
// This is going to throw. Calling it just to get a consistent exception message. // This is going to throw. Calling it just to get a consistent exception message.
AssertCanAddAttribute(); AssertCanAddAttribute();
} }
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -438,7 +410,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="frame">A <see cref="RenderTreeFrame"/> holding the name and value of the attribute.</param> /// <param name="frame">A <see cref="RenderTreeFrame"/> holding the name and value of the attribute.</param>
public void AddAttribute(int sequence, in RenderTreeFrame frame) public void AddAttribute(int sequence, in RenderTreeFrame frame)
{ {
ProfilingStart();
if (frame.FrameType != RenderTreeFrameType.Attribute) if (frame.FrameType != RenderTreeFrameType.Attribute)
{ {
throw new ArgumentException($"The {nameof(frame.FrameType)} must be {RenderTreeFrameType.Attribute}."); throw new ArgumentException($"The {nameof(frame.FrameType)} must be {RenderTreeFrameType.Attribute}.");
@ -446,7 +417,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
AssertCanAddAttribute(); AssertCanAddAttribute();
Append(frame.WithAttributeSequence(sequence)); Append(frame.WithAttributeSequence(sequence));
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -456,7 +426,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="attributes">A collection of key-value pairs representing attributes.</param> /// <param name="attributes">A collection of key-value pairs representing attributes.</param>
public void AddMultipleAttributes(int sequence, IEnumerable<KeyValuePair<string, object>>? attributes) public void AddMultipleAttributes(int sequence, IEnumerable<KeyValuePair<string, object>>? attributes)
{ {
ProfilingStart();
// Calling this up-front just to make sure we validate before mutating anything. // Calling this up-front just to make sure we validate before mutating anything.
AssertCanAddAttribute(); AssertCanAddAttribute();
@ -473,7 +442,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
AddAttribute(sequence, attribute.Key, attribute.Value); AddAttribute(sequence, attribute.Key, attribute.Value);
} }
} }
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -490,7 +458,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="updatesAttributeName">The name of another attribute whose value can be updated when the event handler is executed.</param> /// <param name="updatesAttributeName">The name of another attribute whose value can be updated when the event handler is executed.</param>
public void SetUpdatesAttributeName(string updatesAttributeName) public void SetUpdatesAttributeName(string updatesAttributeName)
{ {
ProfilingStart();
if (_entries.Count == 0) if (_entries.Count == 0)
{ {
throw new InvalidOperationException("No preceding attribute frame exists."); throw new InvalidOperationException("No preceding attribute frame exists.");
@ -503,7 +470,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
} }
prevFrame = prevFrame.WithAttributeEventUpdatesAttributeName(updatesAttributeName); prevFrame = prevFrame.WithAttributeEventUpdatesAttributeName(updatesAttributeName);
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -535,12 +501,10 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="value">The value for the key.</param> /// <param name="value">The value for the key.</param>
public void SetKey(object? value) public void SetKey(object? value)
{ {
ProfilingStart();
if (value == null) if (value == null)
{ {
// Null is equivalent to not having set a key, which is valuable because Razor syntax doesn't have an // Null is equivalent to not having set a key, which is valuable because Razor syntax doesn't have an
// easy way to have conditional directive attributes // easy way to have conditional directive attributes
ProfilingEnd();
return; return;
} }
@ -563,12 +527,10 @@ namespace Microsoft.AspNetCore.Components.Rendering
default: default:
throw new InvalidOperationException($"Cannot set a key on a frame of type {parentFrame.FrameType}."); throw new InvalidOperationException($"Cannot set a key on a frame of type {parentFrame.FrameType}.");
} }
ProfilingEnd();
} }
private void OpenComponentUnchecked(int sequence, Type componentType) private void OpenComponentUnchecked(int sequence, Type componentType)
{ {
ProfilingStart();
// We are entering a new scope, since we track the "duplicate attributes" per // We are entering a new scope, since we track the "duplicate attributes" per
// element/component we might need to clean them up now. // element/component we might need to clean them up now.
if (_hasSeenAddMultipleAttributes) if (_hasSeenAddMultipleAttributes)
@ -579,7 +541,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
_openElementIndices.Push(_entries.Count); _openElementIndices.Push(_entries.Count);
Append(RenderTreeFrame.ChildComponent(sequence, componentType)); Append(RenderTreeFrame.ChildComponent(sequence, componentType));
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -588,7 +549,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// </summary> /// </summary>
public void CloseComponent() public void CloseComponent()
{ {
ProfilingStart();
var indexOfEntryBeingClosed = _openElementIndices.Pop(); var indexOfEntryBeingClosed = _openElementIndices.Pop();
// We might be closing a component with only attributes. Run the attribute cleanup pass // We might be closing a component with only attributes. Run the attribute cleanup pass
@ -600,7 +560,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed]; ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed];
entry = entry.WithComponentSubtreeLength(_entries.Count - indexOfEntryBeingClosed); entry = entry.WithComponentSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -610,14 +569,12 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="elementReferenceCaptureAction">An action to be invoked whenever the reference value changes.</param> /// <param name="elementReferenceCaptureAction">An action to be invoked whenever the reference value changes.</param>
public void AddElementReferenceCapture(int sequence, Action<ElementReference> elementReferenceCaptureAction) public void AddElementReferenceCapture(int sequence, Action<ElementReference> elementReferenceCaptureAction)
{ {
ProfilingStart();
if (GetCurrentParentFrameType() != RenderTreeFrameType.Element) if (GetCurrentParentFrameType() != RenderTreeFrameType.Element)
{ {
throw new InvalidOperationException($"Element reference captures may only be added as children of frames of type {RenderTreeFrameType.Element}"); throw new InvalidOperationException($"Element reference captures may only be added as children of frames of type {RenderTreeFrameType.Element}");
} }
Append(RenderTreeFrame.ElementReferenceCapture(sequence, elementReferenceCaptureAction)); Append(RenderTreeFrame.ElementReferenceCapture(sequence, elementReferenceCaptureAction));
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -627,7 +584,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="componentReferenceCaptureAction">An action to be invoked whenever the reference value changes.</param> /// <param name="componentReferenceCaptureAction">An action to be invoked whenever the reference value changes.</param>
public void AddComponentReferenceCapture(int sequence, Action<object?> componentReferenceCaptureAction) public void AddComponentReferenceCapture(int sequence, Action<object?> componentReferenceCaptureAction)
{ {
ProfilingStart();
var parentFrameIndex = GetCurrentParentFrameIndex(); var parentFrameIndex = GetCurrentParentFrameIndex();
if (!parentFrameIndex.HasValue) if (!parentFrameIndex.HasValue)
{ {
@ -641,7 +597,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
} }
Append(RenderTreeFrame.ComponentReferenceCapture(sequence, componentReferenceCaptureAction, parentFrameIndexValue)); Append(RenderTreeFrame.ComponentReferenceCapture(sequence, componentReferenceCaptureAction, parentFrameIndexValue));
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -650,7 +605,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="sequence">An integer that represents the position of the instruction in the source code.</param> /// <param name="sequence">An integer that represents the position of the instruction in the source code.</param>
public void OpenRegion(int sequence) public void OpenRegion(int sequence)
{ {
ProfilingStart();
// We are entering a new scope, since we track the "duplicate attributes" per // We are entering a new scope, since we track the "duplicate attributes" per
// element/component we might need to clean them up now. // element/component we might need to clean them up now.
if (_hasSeenAddMultipleAttributes) if (_hasSeenAddMultipleAttributes)
@ -661,7 +615,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
_openElementIndices.Push(_entries.Count); _openElementIndices.Push(_entries.Count);
Append(RenderTreeFrame.Region(sequence)); Append(RenderTreeFrame.Region(sequence));
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -670,11 +623,9 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// </summary> /// </summary>
public void CloseRegion() public void CloseRegion()
{ {
ProfilingStart();
var indexOfEntryBeingClosed = _openElementIndices.Pop(); var indexOfEntryBeingClosed = _openElementIndices.Pop();
ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed]; ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed];
entry = entry.WithRegionSubtreeLength(_entries.Count - indexOfEntryBeingClosed); entry = entry.WithRegionSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
ProfilingEnd();
} }
private void AssertCanAddAttribute() private void AssertCanAddAttribute()
@ -702,29 +653,24 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// </summary> /// </summary>
public void Clear() public void Clear()
{ {
ProfilingStart();
_entries.Clear(); _entries.Clear();
_openElementIndices.Clear(); _openElementIndices.Clear();
_lastNonAttributeFrameType = null; _lastNonAttributeFrameType = null;
_hasSeenAddMultipleAttributes = false; _hasSeenAddMultipleAttributes = false;
_seenAttributeNames?.Clear(); _seenAttributeNames?.Clear();
ProfilingEnd();
} }
// internal because this should only be used during the post-event tree patching logic // internal because this should only be used during the post-event tree patching logic
// It's expensive because it involves copying all the subsequent memory in the array // It's expensive because it involves copying all the subsequent memory in the array
internal void InsertAttributeExpensive(int insertAtIndex, int sequence, string attributeName, object? attributeValue) internal void InsertAttributeExpensive(int insertAtIndex, int sequence, string attributeName, object? attributeValue)
{ {
ProfilingStart();
// Replicate the same attribute omission logic as used elsewhere // Replicate the same attribute omission logic as used elsewhere
if ((attributeValue == null) || (attributeValue is bool boolValue && !boolValue)) if ((attributeValue == null) || (attributeValue is bool boolValue && !boolValue))
{ {
ProfilingEnd();
return; return;
} }
_entries.InsertExpensive(insertAtIndex, RenderTreeFrame.Attribute(sequence, attributeName, attributeValue)); _entries.InsertExpensive(insertAtIndex, RenderTreeFrame.Attribute(sequence, attributeName, attributeValue));
ProfilingEnd();
} }
/// <summary> /// <summary>
@ -748,7 +694,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
// Internal for testing // Internal for testing
internal void ProcessDuplicateAttributes(int first) internal void ProcessDuplicateAttributes(int first)
{ {
ProfilingStart();
Debug.Assert(_hasSeenAddMultipleAttributes); Debug.Assert(_hasSeenAddMultipleAttributes);
// When AddMultipleAttributes method has been called, we need to postprocess attributes while closing // When AddMultipleAttributes method has been called, we need to postprocess attributes while closing
@ -830,7 +775,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
seenAttributeNames.Clear(); seenAttributeNames.Clear();
_hasSeenAddMultipleAttributes = false; _hasSeenAddMultipleAttributes = false;
ProfilingEnd();
} }
// Internal for testing // Internal for testing
@ -847,22 +791,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
void IDisposable.Dispose() void IDisposable.Dispose()
{ {
ProfilingStart();
_entries.Dispose(); _entries.Dispose();
ProfilingEnd();
} }
// Having too many calls to ComponentsProfiling.Instance.Start/End has a measurable perf impact
// even when capturing is disabled. So, to enable detailed profiling for this class, define the
// Profile_RenderTreeBuilder compiler symbol, otherwise the calls are compiled out entirely.
// Enabling detailed profiling adds about 5% to rendering benchmark times.
[Conditional("Profile_RenderTreeBuilder")]
private static void ProfilingStart([CallerMemberName] string? name = null)
=> ComponentsProfiling.Instance.Start(name);
[Conditional("Profile_RenderTreeBuilder")]
private static void ProfilingEnd([CallerMemberName] string? name = null)
=> ComponentsProfiling.Instance.End(name);
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,6 @@ import { setEventDispatcher } from './Rendering/RendererEventDispatcher';
import { resolveOptions, CircuitStartOptions } from './Platform/Circuits/CircuitStartOptions'; import { resolveOptions, CircuitStartOptions } from './Platform/Circuits/CircuitStartOptions';
import { DefaultReconnectionHandler } from './Platform/Circuits/DefaultReconnectionHandler'; import { DefaultReconnectionHandler } from './Platform/Circuits/DefaultReconnectionHandler';
import { attachRootComponentToLogicalElement } from './Rendering/Renderer'; import { attachRootComponentToLogicalElement } from './Rendering/Renderer';
import { initializeProfiling } from './Platform/Profiling';
let renderingFailed = false; let renderingFailed = false;
let started = false; let started = false;
@ -22,7 +21,6 @@ async function boot(userOptions?: Partial<CircuitStartOptions>): Promise<void> {
throw new Error('Blazor has already started.'); throw new Error('Blazor has already started.');
} }
started = true; started = true;
initializeProfiling(null);
// Establish options to be used // Establish options to be used
const options = resolveOptions(userOptions); const options = resolveOptions(userOptions);

View File

@ -11,7 +11,6 @@ import { WebAssemblyConfigLoader } from './Platform/WebAssemblyConfigLoader';
import { BootConfigResult } from './Platform/BootConfig'; import { BootConfigResult } from './Platform/BootConfig';
import { Pointer } from './Platform/Platform'; import { Pointer } from './Platform/Platform';
import { WebAssemblyStartOptions } from './Platform/WebAssemblyStartOptions'; import { WebAssemblyStartOptions } from './Platform/WebAssemblyStartOptions';
import { profileStart, profileEnd } from './Platform/Profiling';
let started = false; let started = false;
@ -34,8 +33,6 @@ async function boot(options?: Partial<WebAssemblyStartOptions>): Promise<void> {
const platform = Environment.setPlatform(monoPlatform); const platform = Environment.setPlatform(monoPlatform);
window['Blazor'].platform = platform; window['Blazor'].platform = platform;
window['Blazor']._internal.renderBatch = (browserRendererId: number, batchAddress: Pointer) => { window['Blazor']._internal.renderBatch = (browserRendererId: number, batchAddress: Pointer) => {
profileStart('renderBatch');
// We're going to read directly from the .NET memory heap, so indicate to the platform // We're going to read directly from the .NET memory heap, so indicate to the platform
// that we don't want anything to modify the memory contents during this time. Currently this // that we don't want anything to modify the memory contents during this time. Currently this
// is only guaranteed by the fact that .NET code doesn't run during this time, but in the // is only guaranteed by the fact that .NET code doesn't run during this time, but in the
@ -47,8 +44,6 @@ async function boot(options?: Partial<WebAssemblyStartOptions>): Promise<void> {
} finally { } finally {
heapLock.release(); heapLock.release();
} }
profileEnd('renderBatch');
}; };
// Configure navigation via JS Interop // Configure navigation via JS Interop

View File

@ -1,7 +1,6 @@
import { navigateTo, internalFunctions as navigationManagerInternalFunctions } from './Services/NavigationManager'; import { navigateTo, internalFunctions as navigationManagerInternalFunctions } from './Services/NavigationManager';
import { attachRootComponentToElement } from './Rendering/Renderer'; import { attachRootComponentToElement } from './Rendering/Renderer';
import { domFunctions } from './DomWrapper'; import { domFunctions } from './DomWrapper';
import { setProfilingEnabled } from './Platform/Profiling';
// Make the following APIs available in global scope for invocation from JS // Make the following APIs available in global scope for invocation from JS
window['Blazor'] = { window['Blazor'] = {
@ -11,6 +10,5 @@ window['Blazor'] = {
attachRootComponentToElement, attachRootComponentToElement,
navigationManager: navigationManagerInternalFunctions, navigationManager: navigationManagerInternalFunctions,
domWrapper: domFunctions, domWrapper: domFunctions,
setProfilingEnabled: setProfilingEnabled,
}, },
}; };

View File

@ -5,7 +5,6 @@ import { WebAssemblyResourceLoader, LoadingResource } from '../WebAssemblyResour
import { Platform, System_Array, Pointer, System_Object, System_String, HeapLock } from '../Platform'; import { Platform, System_Array, Pointer, System_Object, System_String, HeapLock } from '../Platform';
import { loadTimezoneData } from './TimezoneDataFile'; import { loadTimezoneData } from './TimezoneDataFile';
import { WebAssemblyBootResourceType } from '../WebAssemblyStartOptions'; import { WebAssemblyBootResourceType } from '../WebAssemblyStartOptions';
import { initializeProfiling } from '../Profiling';
let mono_wasm_add_assembly: (name: string, heapAddress: number, length: number) => void; let mono_wasm_add_assembly: (name: string, heapAddress: number, length: number) => void;
const appBinDirName = 'appBinDir'; const appBinDirName = 'appBinDir';
@ -36,10 +35,6 @@ export const monoPlatform: Platform = {
start: function start(resourceLoader: WebAssemblyResourceLoader) { start: function start(resourceLoader: WebAssemblyResourceLoader) {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
attachDebuggerHotkey(resourceLoader); attachDebuggerHotkey(resourceLoader);
initializeProfiling(isCapturing => {
const setCapturingMethod = bindStaticMethod('Microsoft.AspNetCore.Components', 'Microsoft.AspNetCore.Components.Profiling.WebAssemblyComponentsProfiling', 'SetCapturing');
setCapturingMethod(isCapturing);
});
// dotnet.js assumes the existence of this // dotnet.js assumes the existence of this
window['Browser'] = { window['Browser'] = {

View File

@ -1,137 +0,0 @@
// Import type definitions to ensure that the global declaration
// is of BINDING is included when tests run
import './Mono/MonoTypes';
import { System_String } from './Platform';
interface TimingEntry {
// To minimize overhead, don't even decode the strings that arrive from .NET. Assume they are compile-time constants
// and hence the memory address will be fixed, so we can just store the pointer value.
name: string | System_String;
type: 'start' | 'end';
timestamp: number;
}
interface TraceEvent {
// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
name: string;
cat: string; // Category
ph: 'B' | 'E'; // Phase
ts: number; // Timestamp in microseconds
pid: number; // Process ID
tid: number; // Thread ID
}
let updateCapturingStateInHost: (isCapturing: boolean) => void;
let captureStartTime = 0;
const blazorProfilingEnabledKey = 'blazorProfilingEnabled';
const profilingEnabled = !!localStorage[blazorProfilingEnabledKey];
const entryLog: TimingEntry[] = [];
const openRegionsStack: (string | System_String)[] = [];
export function setProfilingEnabled(enabled: boolean) {
// We only wire up the hotkeys etc. if the following localStorage entry is present during startup
// This is to ensure we're not interfering with any other hotkeys that developers might want to
// use for different purposes, plus it gives us a single point where we can notify .NET code during
// startup about whether profiling should be enabled.
localStorage[blazorProfilingEnabledKey] = (enabled !== false);
location.reload();
}
export function initializeProfiling(setCapturingCallback: ((isCapturing: boolean) => void) | null) {
if (!profilingEnabled) {
return;
}
updateCapturingStateInHost = setCapturingCallback || (() => {});
// Attach hotkey (alt/cmd)+shift+p
const altKeyName = navigator.platform.match(/^Mac/i) ? 'Cmd' : 'Alt';
console.info(`Profiling hotkey: Shift+${altKeyName}+P (when application has focus)`);
document.addEventListener('keydown', evt => {
if (evt.shiftKey && (evt.metaKey || evt.altKey) && evt.code === 'KeyP') {
toggleCaptureEnabled();
}
});
}
export function profileStart(name: System_String | string) {
if (!captureStartTime) {
return;
}
const startTime = performance.now();
openRegionsStack.push(name);
entryLog.push({ name: name, type: 'start', timestamp: startTime });
}
export function profileEnd(name: System_String | string) {
if (!captureStartTime) {
return;
}
const endTime = performance.now();
const poppedRegionName = openRegionsStack.pop();
if (!poppedRegionName) {
throw new Error(`Profiling mismatch: tried to end profiling for '${readJsString(name)}', but the stack was empty.`);
} else if (poppedRegionName !== name) {
throw new Error(`Profiling mismatch: tried to end profiling for '${readJsString(name)}', but the top stack item was '${readJsString(poppedRegionName)}'.`);
}
entryLog.push({ name: name, type: 'end', timestamp: endTime });
}
function profileReset() {
openRegionsStack.length = 0;
entryLog.length = 0;
captureStartTime = 0;
updateCapturingStateInHost(false);
}
function profileExport() {
const traceEvents: TraceEvent[] = entryLog.map(entry => ({
name: readJsString(entry.name)!,
cat: 'PERF',
ph: entry.type === 'start' ? 'B': 'E',
ts: (entry.timestamp - captureStartTime) * 1000,
pid: 0,
tid: 0,
}));
const traceEventsJson = JSON.stringify(traceEvents);
const traceEventsBuffer = new TextEncoder().encode(traceEventsJson);
const anchorElement = document.createElement('a');
anchorElement.href = URL.createObjectURL(new Blob([traceEventsBuffer]));
anchorElement.setAttribute('download', 'trace.json');
anchorElement.click();
URL.revokeObjectURL(anchorElement.href);
}
function toggleCaptureEnabled() {
if (!captureStartTime) {
displayOverlayMessage('Started capturing performance profile...');
updateCapturingStateInHost(true);
captureStartTime = performance.now();
} else {
displayOverlayMessage('Finished capturing performance profile');
profileExport();
profileReset();
}
}
function displayOverlayMessage(message: string) {
const elem = document.createElement('div');
elem.textContent = message;
elem.setAttribute('style', 'position: absolute; z-index: 99999; font-family: \'Sans Serif\'; top: 0; left: 0; padding: 4px; font-size: 12px; background-color: purple; color: white;');
document.body.appendChild(elem);
setTimeout(() => document.body.removeChild(elem), 3000);
}
function readJsString(str: string | System_String) {
// This is expensive, so don't do it while capturing timings. Only do it as part of the export process.
return typeof str === 'string' ? str : BINDING.conv_string(str);
}
// These globals deliberately differ from our normal conventions for attaching functions inside Blazor.*
// because the intention is to minimize overhead in all reasonable ways. Having any dot-separators in the
// name would cause extra string allocations on every invocation.
window['_blazorProfileStart'] = profileStart;
window['_blazorProfileEnd'] = profileEnd;

View File

@ -6,7 +6,6 @@ import { applyCaptureIdToElement } from './ElementReferenceCapture';
import { EventFieldInfo } from './EventFieldInfo'; import { EventFieldInfo } from './EventFieldInfo';
import { dispatchEvent } from './RendererEventDispatcher'; import { dispatchEvent } from './RendererEventDispatcher';
import { attachToEventDelegator as attachNavigationManagerToEventDelegator } from '../Services/NavigationManager'; import { attachToEventDelegator as attachNavigationManagerToEventDelegator } from '../Services/NavigationManager';
import { profileEnd, profileStart } from '../Platform/Profiling';
const selectValuePropname = '_blazorSelectValue'; const selectValuePropname = '_blazorSelectValue';
const sharedTemplateElemForParsing = document.createElement('template'); const sharedTemplateElemForParsing = document.createElement('template');
const sharedSvgElemForParsing = document.createElementNS('http://www.w3.org/2000/svg', 'g'); const sharedSvgElemForParsing = document.createElementNS('http://www.w3.org/2000/svg', 'g');
@ -41,8 +40,6 @@ export class BrowserRenderer {
} }
public updateComponent(batch: RenderBatch, componentId: number, edits: ArrayBuilderSegment<RenderTreeEdit>, referenceFrames: ArrayValues<RenderTreeFrame>): void { public updateComponent(batch: RenderBatch, componentId: number, edits: ArrayBuilderSegment<RenderTreeEdit>, referenceFrames: ArrayValues<RenderTreeFrame>): void {
profileStart('updateComponent');
const element = this.childComponentLocations[componentId]; const element = this.childComponentLocations[componentId];
if (!element) { if (!element) {
throw new Error(`No element is currently associated with component ${componentId}`); throw new Error(`No element is currently associated with component ${componentId}`);
@ -70,8 +67,6 @@ export class BrowserRenderer {
if ((activeElementBefore instanceof HTMLElement) && ownerDocument && ownerDocument.activeElement !== activeElementBefore) { if ((activeElementBefore instanceof HTMLElement) && ownerDocument && ownerDocument.activeElement !== activeElementBefore) {
activeElementBefore.focus(); activeElementBefore.focus();
} }
profileEnd('updateComponent');
} }
public disposeComponent(componentId: number) { public disposeComponent(componentId: number) {

View File

@ -9,7 +9,6 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="$(ComponentsSharedSourceRoot)src\WebAssemblyJSInteropInternalCalls.cs" />
<Reference Include="Microsoft.JSInterop" /> <Reference Include="Microsoft.JSInterop" />
</ItemGroup> </ItemGroup>