From b654051748e3082ab35b9d82b14a4dd8a4ca8deb Mon Sep 17 00:00:00 2001 From: Pranav K <prkrishn@hotmail.com> Date: Wed, 5 Aug 2020 12:45:42 -0700 Subject: [PATCH] Remove Blazor internal profiling infrastructure (#24468) (#24589) * Put back InternalCalls * Removing .NET profiling calls * Remove JS side profiling Co-authored-by: Steve Sanderson <SteveSandersonMS@users.noreply.github.com> --- .../Microsoft.AspNetCore.Components.csproj | 1 - .../src/Profiling/ComponentsProfiling.cs | 23 --- .../src/Profiling/NoOpComponentsProfiling.cs | 16 --- .../WebAssemblyComponentsProfiling.cs | 41 ------ .../src/RenderTree/RenderTreeDiffBuilder.cs | 47 ------ .../Components/src/RenderTree/Renderer.cs | 10 -- .../src/Rendering/ComponentState.cs | 5 - .../src/Rendering/RenderTreeBuilder.cs | 71 ---------- .../Web.JS/dist/Release/blazor.server.js | Bin 225017 -> 222909 bytes .../Web.JS/dist/Release/blazor.webassembly.js | Bin 65146 -> 62847 bytes src/Components/Web.JS/src/Boot.Server.ts | 2 - src/Components/Web.JS/src/Boot.WebAssembly.ts | 5 - src/Components/Web.JS/src/GlobalExports.ts | 2 - .../Web.JS/src/Platform/Mono/MonoPlatform.ts | 5 - .../Web.JS/src/Platform/Profiling.ts | 134 ------------------ .../Web.JS/src/Rendering/BrowserRenderer.ts | 5 - .../JSInterop/src/InternalCalls.cs} | 0 .../Microsoft.JSInterop.WebAssembly.csproj | 1 - 18 files changed, 368 deletions(-) delete mode 100644 src/Components/Components/src/Profiling/ComponentsProfiling.cs delete mode 100644 src/Components/Components/src/Profiling/NoOpComponentsProfiling.cs delete mode 100644 src/Components/Components/src/Profiling/WebAssemblyComponentsProfiling.cs delete mode 100644 src/Components/Web.JS/src/Platform/Profiling.ts rename src/Components/{Shared/src/WebAssemblyJSInteropInternalCalls.cs => WebAssembly/JSInterop/src/InternalCalls.cs} (100%) diff --git a/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj b/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj index 7565534a51d..1e7f881ed50 100644 --- a/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj +++ b/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj @@ -10,7 +10,6 @@ <ItemGroup> <Compile Include="$(ComponentsSharedSourceRoot)src\ArrayBuilder.cs" LinkBase="RenderTree" /> - <Compile Include="$(ComponentsSharedSourceRoot)src\WebAssemblyJSInteropInternalCalls.cs" /> </ItemGroup> <ItemGroup> diff --git a/src/Components/Components/src/Profiling/ComponentsProfiling.cs b/src/Components/Components/src/Profiling/ComponentsProfiling.cs deleted file mode 100644 index f47a0c917c8..00000000000 --- a/src/Components/Components/src/Profiling/ComponentsProfiling.cs +++ /dev/null @@ -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); - } -} diff --git a/src/Components/Components/src/Profiling/NoOpComponentsProfiling.cs b/src/Components/Components/src/Profiling/NoOpComponentsProfiling.cs deleted file mode 100644 index 74037e7de74..00000000000 --- a/src/Components/Components/src/Profiling/NoOpComponentsProfiling.cs +++ /dev/null @@ -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) - { - } - } -} diff --git a/src/Components/Components/src/Profiling/WebAssemblyComponentsProfiling.cs b/src/Components/Components/src/Profiling/WebAssemblyComponentsProfiling.cs deleted file mode 100644 index 4e0b6dbd745..00000000000 --- a/src/Components/Components/src/Profiling/WebAssemblyComponentsProfiling.cs +++ /dev/null @@ -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); - } - } - } -} diff --git a/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs b/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs index a7892991f45..7429cf7d741 100644 --- a/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs +++ b/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; -using Microsoft.AspNetCore.Components.Profiling; using Microsoft.AspNetCore.Components.Rendering; namespace Microsoft.AspNetCore.Components.RenderTree @@ -28,7 +27,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree ArrayRange<RenderTreeFrame> oldTree, ArrayRange<RenderTreeFrame> newTree) { - ComponentsProfiling.Instance.Start(); var editsBuffer = batchBuilder.EditsBuffer; var editsBufferStartLength = editsBuffer.Count; @@ -37,7 +35,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree var editsSegment = editsBuffer.ToSegment(editsBufferStartLength, editsBuffer.Count); var result = new RenderTreeDiff(componentId, editsSegment); - ComponentsProfiling.Instance.End(); return result; } @@ -49,7 +46,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree int oldStartIndex, int oldEndIndexExcl, int newStartIndex, int newEndIndexExcl) { - ProfilingStart(); // 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, // 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); } } - ProfilingEnd(); } private static Dictionary<object, KeyedItemInfo> BuildKeyToInfoLookup(DiffContext diffContext, int oldStartIndex, int oldEndIndexExcl, int newStartIndex, int newEndIndexExcl) { - ProfilingStart(); var result = diffContext.KeyedItemInfoDictionaryPool.Get(); var oldTree = diffContext.OldTree; var newTree = diffContext.NewTree; @@ -351,7 +345,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree newStartIndex = NextSiblingIndex(frame, newStartIndex); } - ProfilingEnd(); return result; } @@ -384,7 +377,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree int oldStartIndex, int oldEndIndexExcl, int newStartIndex, int newEndIndexExcl) { - ProfilingStart(); // 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 // a hash-join if not. We'll do a merge join until we hit a case we can't handle and @@ -433,7 +425,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree ref diffContext, oldStartIndex, oldEndIndexExcl, newStartIndex, newEndIndexExcl); - ProfilingEnd(); return; } @@ -459,12 +450,9 @@ namespace Microsoft.AspNetCore.Components.RenderTree ref diffContext, oldStartIndex, oldEndIndexExcl, newStartIndex, newEndIndexExcl); - ProfilingEnd(); return; } } - - ProfilingEnd(); } private static void AppendAttributeDiffEntriesForRangeSlow( @@ -472,7 +460,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree int oldStartIndex, int oldEndIndexExcl, int newStartIndex, int newEndIndexExcl) { - ProfilingStart(); var oldTree = diffContext.OldTree; var newTree = diffContext.NewTree; @@ -511,7 +498,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree // We should have processed any additions at this point. Reset for the next batch. diffContext.AttributeDiffSet.Clear(); - ProfilingEnd(); } private static void UpdateRetainedChildComponent( @@ -519,7 +505,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree int oldComponentIndex, int newComponentIndex) { - ProfilingStart(); var oldTree = diffContext.OldTree; var newTree = diffContext.NewTree; ref var oldComponentFrame = ref oldTree[oldComponentIndex]; @@ -546,8 +531,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree { componentState.SetDirectParameters(newParameters); } - - ProfilingEnd(); } private static int NextSiblingIndex(in RenderTreeFrame frame, int frameIndex) @@ -570,7 +553,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree int oldFrameIndex, int newFrameIndex) { - ProfilingStart(); var oldTree = diffContext.OldTree; var newTree = diffContext.NewTree; ref var oldFrame = ref oldTree[oldFrameIndex]; @@ -583,7 +565,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree { InsertNewFrame(ref diffContext, newFrameIndex); RemoveOldFrame(ref diffContext, oldFrameIndex); - ProfilingEnd(); return; } @@ -709,8 +690,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree default: 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 @@ -720,7 +699,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree int oldFrameIndex, int newFrameIndex) { - ProfilingStart(); var oldTree = diffContext.OldTree; var newTree = diffContext.NewTree; ref var oldFrame = ref oldTree[oldFrameIndex]; @@ -749,13 +727,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree // since it was unchanged. newFrame = oldFrame; } - - ProfilingEnd(); } private static void InsertNewFrame(ref DiffContext diffContext, int newFrameIndex) { - ProfilingStart(); var newTree = diffContext.NewTree; ref var newFrame = ref newTree[newFrameIndex]; switch (newFrame.FrameType) @@ -808,12 +783,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree default: throw new NotImplementedException($"Unexpected frame type during {nameof(InsertNewFrame)}: {newFrame.FrameType}"); } - ProfilingEnd(); } private static void RemoveOldFrame(ref DiffContext diffContext, int oldFrameIndex) { - ProfilingStart(); var oldTree = diffContext.OldTree; ref var oldFrame = ref oldTree[oldFrameIndex]; switch (oldFrame.FrameType) @@ -855,7 +828,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree default: throw new NotImplementedException($"Unexpected frame type during {nameof(RemoveOldFrame)}: {oldFrame.FrameType}"); } - ProfilingEnd(); } private static int GetAttributesEndIndexExclusive(RenderTreeFrame[] tree, int rootIndex) @@ -889,7 +861,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree private static void InitializeNewSubtree(ref DiffContext diffContext, int frameIndex) { - ProfilingStart(); var frames = diffContext.NewTree; var endIndexExcl = frameIndex + frames[frameIndex].ElementSubtreeLength; for (var i = frameIndex; i < endIndexExcl; i++) @@ -911,12 +882,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree break; } } - ProfilingEnd(); } private static void InitializeNewComponentFrame(ref DiffContext diffContext, int frameIndex) { - ProfilingStart(); var frames = diffContext.NewTree; ref var frame = ref frames[frameIndex]; @@ -933,7 +902,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree var initialParametersLifetime = new ParameterViewLifetime(diffContext.BatchBuilder); var initialParameters = new ParameterView(initialParametersLifetime, frames, frameIndex); childComponentState.SetDirectParameters(initialParameters); - ProfilingEnd(); } private static void InitializeNewAttributeFrame(ref DiffContext diffContext, ref RenderTreeFrame newFrame) @@ -978,7 +946,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree private static void DisposeFramesInRange(RenderBatchBuilder batchBuilder, RenderTreeFrame[] frames, int startIndex, int endIndexExcl) { - ProfilingStart(); for (var i = startIndex; i < endIndexExcl; i++) { ref var frame = ref frames[i]; @@ -991,7 +958,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree batchBuilder.DisposedEventHandlerIds.Append(frame.AttributeEventHandlerId); } } - ProfilingEnd(); } /// <summary> @@ -1033,18 +999,5 @@ namespace Microsoft.AspNetCore.Components.RenderTree 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); } } diff --git a/src/Components/Components/src/RenderTree/Renderer.cs b/src/Components/Components/src/RenderTree/Renderer.cs index 446243201ff..2f1e74a95cd 100644 --- a/src/Components/Components/src/RenderTree/Renderer.cs +++ b/src/Components/Components/src/RenderTree/Renderer.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Components.Profiling; using Microsoft.AspNetCore.Components.Rendering; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -247,7 +246,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree /// </returns> public virtual Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo fieldInfo, EventArgs eventArgs) { - ComponentsProfiling.Instance.Start(); Dispatcher.AssertAccess(); if (!_eventBindings.TryGetValue(eventHandlerId, out var callback)) @@ -275,7 +273,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree catch (Exception e) { HandleException(e); - ComponentsProfiling.Instance.End(); return Task.CompletedTask; } finally @@ -290,7 +287,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree // 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. var result = GetErrorHandledTask(task); - ComponentsProfiling.Instance.End(); return result; } @@ -441,7 +437,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree private void ProcessRenderQueue() { - ComponentsProfiling.Instance.Start(); Dispatcher.AssertAccess(); if (_isBatchInProgress) @@ -456,7 +451,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree { if (_batchBuilder.ComponentRenderQueue.Count == 0) { - ComponentsProfiling.Instance.End(); return; } @@ -468,9 +462,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree } var batch = _batchBuilder.ToBatch(); - ComponentsProfiling.Instance.Start(nameof(UpdateDisplayAsync)); updateDisplayTask = UpdateDisplayAsync(batch); - ComponentsProfiling.Instance.End(nameof(UpdateDisplayAsync)); // Fire off the execution of OnAfterRenderAsync, but don't wait for it // 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. HandleException(e); - ComponentsProfiling.Instance.End(); return; } finally @@ -498,7 +489,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree { ProcessRenderQueue(); } - ComponentsProfiling.Instance.End(); } private Task InvokeRenderCompletedCalls(ArrayRange<RenderTreeDiff> updatedComponents, Task updateDisplayTask) diff --git a/src/Components/Components/src/Rendering/ComponentState.cs b/src/Components/Components/src/Rendering/ComponentState.cs index 7b755efd5df..3655d493350 100644 --- a/src/Components/Components/src/Rendering/ComponentState.cs +++ b/src/Components/Components/src/Rendering/ComponentState.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Microsoft.AspNetCore.Components.Profiling; using Microsoft.AspNetCore.Components.RenderTree; namespace Microsoft.AspNetCore.Components.Rendering @@ -57,7 +56,6 @@ namespace Microsoft.AspNetCore.Components.Rendering public void RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment) { - ComponentsProfiling.Instance.Start(); // 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. if (_componentWasDisposed) @@ -69,9 +67,7 @@ namespace Microsoft.AspNetCore.Components.Rendering (CurrentRenderTree, _renderTreeBuilderPrevious) = (_renderTreeBuilderPrevious, CurrentRenderTree); CurrentRenderTree.Clear(); - ComponentsProfiling.Instance.Start("BuildRenderTree"); renderFragment(CurrentRenderTree); - ComponentsProfiling.Instance.End("BuildRenderTree"); var diff = RenderTreeDiffBuilder.ComputeDiff( _renderer, @@ -81,7 +77,6 @@ namespace Microsoft.AspNetCore.Components.Rendering CurrentRenderTree.GetFrames()); batchBuilder.UpdatedComponentDiffs.Append(diff); batchBuilder.InvalidateParameterViews(); - ComponentsProfiling.Instance.End(); } public bool TryDisposeInBatch(RenderBatchBuilder batchBuilder, [NotNullWhen(false)] out Exception? exception) diff --git a/src/Components/Components/src/Rendering/RenderTreeBuilder.cs b/src/Components/Components/src/Rendering/RenderTreeBuilder.cs index 4e9280c70f4..566265c9888 100644 --- a/src/Components/Components/src/Rendering/RenderTreeBuilder.cs +++ b/src/Components/Components/src/Rendering/RenderTreeBuilder.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; -using Microsoft.AspNetCore.Components.Profiling; using Microsoft.AspNetCore.Components.RenderTree; 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> public void OpenElement(int sequence, string elementName) { - ProfilingStart(); // We are entering a new scope, since we track the "duplicate attributes" per // element/component we might need to clean them up now. if (_hasSeenAddMultipleAttributes) @@ -56,7 +54,6 @@ namespace Microsoft.AspNetCore.Components.Rendering _openElementIndices.Push(_entries.Count); Append(RenderTreeFrame.Element(sequence, elementName)); - ProfilingEnd(); } /// <summary> @@ -65,7 +62,6 @@ namespace Microsoft.AspNetCore.Components.Rendering /// </summary> public void CloseElement() { - ProfilingStart(); var indexOfEntryBeingClosed = _openElementIndices.Pop(); // 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]; entry = entry.WithElementSubtreeLength(_entries.Count - indexOfEntryBeingClosed); - ProfilingEnd(); } /// <summary> @@ -87,9 +82,7 @@ namespace Microsoft.AspNetCore.Components.Rendering /// <param name="markupContent">Content for the new markup frame.</param> public void AddMarkupContent(int sequence, string? markupContent) { - ProfilingStart(); Append(RenderTreeFrame.Markup(sequence, markupContent ?? string.Empty)); - ProfilingEnd(); } /// <summary> @@ -99,9 +92,7 @@ namespace Microsoft.AspNetCore.Components.Rendering /// <param name="textContent">Content for the new text frame.</param> public void AddContent(int sequence, string? textContent) { - ProfilingStart(); Append(RenderTreeFrame.Text(sequence, textContent ?? string.Empty)); - ProfilingEnd(); } /// <summary> @@ -111,7 +102,6 @@ namespace Microsoft.AspNetCore.Components.Rendering /// <param name="fragment">Content to append.</param> public void AddContent(int sequence, RenderFragment? fragment) { - ProfilingStart(); if (fragment != null) { // We surround the fragment with a region delimiter to indicate that the @@ -122,7 +112,6 @@ namespace Microsoft.AspNetCore.Components.Rendering fragment(this); CloseRegion(); } - ProfilingEnd(); } /// <summary> @@ -133,12 +122,10 @@ namespace Microsoft.AspNetCore.Components.Rendering /// <param name="value">The value used by <paramref name="fragment"/>.</param> public void AddContent<TValue>(int sequence, RenderFragment<TValue>? fragment, TValue value) { - ProfilingStart(); if (fragment != null) { AddContent(sequence, fragment(value)); } - ProfilingEnd(); } /// <summary> @@ -169,15 +156,12 @@ namespace Microsoft.AspNetCore.Components.Rendering /// <param name="name">The name of the attribute.</param> public void AddAttribute(int sequence, string name) { - ProfilingStart(); - if (_lastNonAttributeFrameType != RenderTreeFrameType.Element) { throw new InvalidOperationException($"Valueless attributes may only be added immediately after frames of type {RenderTreeFrameType.Element}"); } Append(RenderTreeFrame.Attribute(sequence, name, BoxedTrue)); - ProfilingEnd(); } /// <summary> @@ -194,7 +178,6 @@ namespace Microsoft.AspNetCore.Components.Rendering /// <param name="value">The value of the attribute.</param> public void AddAttribute(int sequence, string name, bool value) { - ProfilingStart(); AssertCanAddAttribute(); if (_lastNonAttributeFrameType == RenderTreeFrameType.Component) { @@ -210,7 +193,6 @@ namespace Microsoft.AspNetCore.Components.Rendering { TrackAttributeName(name); } - ProfilingEnd(); } /// <summary> @@ -227,7 +209,6 @@ namespace Microsoft.AspNetCore.Components.Rendering /// <param name="value">The value of the attribute.</param> public void AddAttribute(int sequence, string name, string? value) { - ProfilingStart(); AssertCanAddAttribute(); if (value != null || _lastNonAttributeFrameType == RenderTreeFrameType.Component) { @@ -237,7 +218,6 @@ namespace Microsoft.AspNetCore.Components.Rendering { TrackAttributeName(name); } - ProfilingEnd(); } /// <summary> @@ -254,7 +234,6 @@ namespace Microsoft.AspNetCore.Components.Rendering /// <param name="value">The value of the attribute.</param> public void AddAttribute(int sequence, string name, MulticastDelegate? value) { - ProfilingStart(); AssertCanAddAttribute(); if (value != null || _lastNonAttributeFrameType == RenderTreeFrameType.Component) { @@ -264,7 +243,6 @@ namespace Microsoft.AspNetCore.Components.Rendering { TrackAttributeName(name); } - ProfilingEnd(); } /// <summary> @@ -285,7 +263,6 @@ namespace Microsoft.AspNetCore.Components.Rendering /// </remarks> public void AddAttribute(int sequence, string name, EventCallback value) { - ProfilingStart(); AssertCanAddAttribute(); if (_lastNonAttributeFrameType == RenderTreeFrameType.Component) { @@ -310,7 +287,6 @@ namespace Microsoft.AspNetCore.Components.Rendering // Track the attribute name if needed since we elided the frame. TrackAttributeName(name); } - ProfilingEnd(); } /// <summary> @@ -331,7 +307,6 @@ namespace Microsoft.AspNetCore.Components.Rendering /// </remarks> public void AddAttribute<TArgument>(int sequence, string name, EventCallback<TArgument> value) { - ProfilingStart(); AssertCanAddAttribute(); if (_lastNonAttributeFrameType == RenderTreeFrameType.Component) { @@ -356,7 +331,6 @@ namespace Microsoft.AspNetCore.Components.Rendering // Track the attribute name if needed since we elided the frame. TrackAttributeName(name); } - ProfilingEnd(); } /// <summary> @@ -370,7 +344,6 @@ namespace Microsoft.AspNetCore.Components.Rendering /// <param name="value">The value of the attribute.</param> 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 // types that AddAttribute special cases. 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. AssertCanAddAttribute(); } - ProfilingEnd(); } /// <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> public void AddAttribute(int sequence, in RenderTreeFrame frame) { - ProfilingStart(); if (frame.FrameType != RenderTreeFrameType.Attribute) { throw new ArgumentException($"The {nameof(frame.FrameType)} must be {RenderTreeFrameType.Attribute}."); @@ -446,7 +417,6 @@ namespace Microsoft.AspNetCore.Components.Rendering AssertCanAddAttribute(); Append(frame.WithAttributeSequence(sequence)); - ProfilingEnd(); } /// <summary> @@ -456,7 +426,6 @@ namespace Microsoft.AspNetCore.Components.Rendering /// <param name="attributes">A collection of key-value pairs representing attributes.</param> public void AddMultipleAttributes(int sequence, IEnumerable<KeyValuePair<string, object>>? attributes) { - ProfilingStart(); // Calling this up-front just to make sure we validate before mutating anything. AssertCanAddAttribute(); @@ -473,7 +442,6 @@ namespace Microsoft.AspNetCore.Components.Rendering AddAttribute(sequence, attribute.Key, attribute.Value); } } - ProfilingEnd(); } /// <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> public void SetUpdatesAttributeName(string updatesAttributeName) { - ProfilingStart(); if (_entries.Count == 0) { throw new InvalidOperationException("No preceding attribute frame exists."); @@ -503,7 +470,6 @@ namespace Microsoft.AspNetCore.Components.Rendering } prevFrame = prevFrame.WithAttributeEventUpdatesAttributeName(updatesAttributeName); - ProfilingEnd(); } /// <summary> @@ -535,12 +501,10 @@ namespace Microsoft.AspNetCore.Components.Rendering /// <param name="value">The value for the key.</param> public void SetKey(object? value) { - ProfilingStart(); if (value == null) { // 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 - ProfilingEnd(); return; } @@ -563,12 +527,10 @@ namespace Microsoft.AspNetCore.Components.Rendering default: throw new InvalidOperationException($"Cannot set a key on a frame of type {parentFrame.FrameType}."); } - ProfilingEnd(); } private void OpenComponentUnchecked(int sequence, Type componentType) { - ProfilingStart(); // We are entering a new scope, since we track the "duplicate attributes" per // element/component we might need to clean them up now. if (_hasSeenAddMultipleAttributes) @@ -579,7 +541,6 @@ namespace Microsoft.AspNetCore.Components.Rendering _openElementIndices.Push(_entries.Count); Append(RenderTreeFrame.ChildComponent(sequence, componentType)); - ProfilingEnd(); } /// <summary> @@ -588,7 +549,6 @@ namespace Microsoft.AspNetCore.Components.Rendering /// </summary> public void CloseComponent() { - ProfilingStart(); var indexOfEntryBeingClosed = _openElementIndices.Pop(); // 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]; entry = entry.WithComponentSubtreeLength(_entries.Count - indexOfEntryBeingClosed); - ProfilingEnd(); } /// <summary> @@ -610,14 +569,12 @@ namespace Microsoft.AspNetCore.Components.Rendering /// <param name="elementReferenceCaptureAction">An action to be invoked whenever the reference value changes.</param> public void AddElementReferenceCapture(int sequence, Action<ElementReference> elementReferenceCaptureAction) { - ProfilingStart(); if (GetCurrentParentFrameType() != 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)); - ProfilingEnd(); } /// <summary> @@ -627,7 +584,6 @@ namespace Microsoft.AspNetCore.Components.Rendering /// <param name="componentReferenceCaptureAction">An action to be invoked whenever the reference value changes.</param> public void AddComponentReferenceCapture(int sequence, Action<object?> componentReferenceCaptureAction) { - ProfilingStart(); var parentFrameIndex = GetCurrentParentFrameIndex(); if (!parentFrameIndex.HasValue) { @@ -641,7 +597,6 @@ namespace Microsoft.AspNetCore.Components.Rendering } Append(RenderTreeFrame.ComponentReferenceCapture(sequence, componentReferenceCaptureAction, parentFrameIndexValue)); - ProfilingEnd(); } /// <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> public void OpenRegion(int sequence) { - ProfilingStart(); // We are entering a new scope, since we track the "duplicate attributes" per // element/component we might need to clean them up now. if (_hasSeenAddMultipleAttributes) @@ -661,7 +615,6 @@ namespace Microsoft.AspNetCore.Components.Rendering _openElementIndices.Push(_entries.Count); Append(RenderTreeFrame.Region(sequence)); - ProfilingEnd(); } /// <summary> @@ -670,11 +623,9 @@ namespace Microsoft.AspNetCore.Components.Rendering /// </summary> public void CloseRegion() { - ProfilingStart(); var indexOfEntryBeingClosed = _openElementIndices.Pop(); ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed]; entry = entry.WithRegionSubtreeLength(_entries.Count - indexOfEntryBeingClosed); - ProfilingEnd(); } private void AssertCanAddAttribute() @@ -702,29 +653,24 @@ namespace Microsoft.AspNetCore.Components.Rendering /// </summary> public void Clear() { - ProfilingStart(); _entries.Clear(); _openElementIndices.Clear(); _lastNonAttributeFrameType = null; _hasSeenAddMultipleAttributes = false; _seenAttributeNames?.Clear(); - ProfilingEnd(); } // 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 internal void InsertAttributeExpensive(int insertAtIndex, int sequence, string attributeName, object? attributeValue) { - ProfilingStart(); // Replicate the same attribute omission logic as used elsewhere if ((attributeValue == null) || (attributeValue is bool boolValue && !boolValue)) { - ProfilingEnd(); return; } _entries.InsertExpensive(insertAtIndex, RenderTreeFrame.Attribute(sequence, attributeName, attributeValue)); - ProfilingEnd(); } /// <summary> @@ -748,7 +694,6 @@ namespace Microsoft.AspNetCore.Components.Rendering // Internal for testing internal void ProcessDuplicateAttributes(int first) { - ProfilingStart(); Debug.Assert(_hasSeenAddMultipleAttributes); // When AddMultipleAttributes method has been called, we need to postprocess attributes while closing @@ -830,7 +775,6 @@ namespace Microsoft.AspNetCore.Components.Rendering seenAttributeNames.Clear(); _hasSeenAddMultipleAttributes = false; - ProfilingEnd(); } // Internal for testing @@ -847,22 +791,7 @@ namespace Microsoft.AspNetCore.Components.Rendering void IDisposable.Dispose() { - ProfilingStart(); _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); } } diff --git a/src/Components/Web.JS/dist/Release/blazor.server.js b/src/Components/Web.JS/dist/Release/blazor.server.js index 32c6455b2002826fbd11c96111043d917ecb96ab..97da9d5ed9498ef67c205008e9f2b51dbdecc0e9 100644 GIT binary patch delta 2000 zcmZuydu)?c6wf(dx6v`!#@Le#+QCLY_*VP1>tkE$cnGp&!ptCn%?<YWx>mNf>4UM1 zp+Ynv4nrJpjVKEkj663ec;f@&goSA`UWx{F2{Cad8i~d~lqDF?Z3~9rKRY|8=l<^R zJLh-Ky+1kY;>4_x0ST<Jz0yI{*=5By_{XlJu`g2XbI>NU=be%vW+>ch09$OJa!(tq z6+gp<LG}Bh!85z>#YW$)P7!YXqCwZ$teE=VErhi(-TTWi9yJWMq@=~VKe$<+LD-FM zzsVshn#Nk^Lt(7_eoetVo2>Xvs#C7s)*3Q|D8X7GD*>{qm0!$;bvk~y7(%l_W!VZe zFIFq%YM<HX_i7<uBrMYGiKDIbzm32}tU~1n6!=(|P2d8`PLMuf?R<+0-z<R*-1!0= zD~7dfvKJPR`=A#ZC2}|R!zpydzq`yv;1#lYMbzWbLJ1tg7dTdntcd#@m5KYDR#|B$ z-)ZBUcfzJzu(0V{c!Ia@hf%%W@(@E*GlwlZ4Ek&W5X5Yv;AGVb-gp>}WIil8Teb_F za|kezmZWd9u<kQ3jQ#!WW(urhY#25vgdmWtNop!l5!9?nYN{(qO;)1r!JWSsu<8+* z$&4dVz*<J2bgswU7ml`d`yy3FOTXfE_w_45(Vl#EW(3~Sdzv(Jq&KKFv(N6pT;6^b zN&&oV&pEKO;25N`sdEsaKvN7<@bbMe*bNY6*%v^?sDWMj2=Z9t1<0QnEh~$fJ%RRU z*u;i%r96J_0z5kldinI{@GUsIsHc7{v}XNtb?(599VLbmrAw|dMV|~u&FukyMDs^v zS@EzLV_*jldwL9BlR8a4W*mn?es>HC5jxrQRk#eD{L(m#0^RiTmry7{2YY)03}il; zfb*0TyatD$mt|asG6=G>47TvW>u>}0$>LjGyz3U6SPd?oUx+<`_Da652$!ZlYSyFy z3+AbEwPx0~2LmCJWp`Z7zJy3rmSx0za59q-SL>-C(RjyMj}gragq}VBr)MuEIjro8 z5!dKR#=Yd)Dp_s`F{tEkl;9AO%FLzMM(&%XIGvVo^VoPfX7Y38I5}?y-9_3;jBzRd zv=$3;iN@^~bg*BhD642Q9+be&&$i)0nlOGlh&!NKP!e^2V#K=KeH~gbq_ww3v?^lf zlKT~p;6?Wcx@rX*2%!au=cN!XmPmr^#S$#yQW)>)i07X@_*+g<eBvDr*&Nm)wcXuX zhmY`xXkzRPtY`q|>gjBJ2~={|0ETr$^3*=`N;663@i@(mP-!l&*pK5>3jW|AzMl4y zHL(oDFw!81Lc7Rz36d(wMOYdpLDoq&C@dj%kX6Zc3ZL38iY*3boAAlD2}_K+$<nbN zw^3DA(VuEh)ZZTQ1^k8vlP33tG<IwnmN3^4*1;<F>JU10lxE>S451HTC0l(2uN3yw zG+7m++FWJwxiz__$);GFtC}j5m3-k*l%%wkCQXSbes*LU%)IFsekDN<d+Ip0WzxMC zrPkz8D1knTy)sS1_2F?Gg<8J-1f~PjvfU?fML~n&F^9c@XjjMbXe7`OX$?h8a#>je zt9MF9{?ADa>cPna7w{CM(EbR!4`jmvUc)h*m0W%X{`FNHPQgl<-8hdu?7;*s$$Wen zI#|n2$m4a_@U)KVPYW+MmDH7W2melj<nZF}aBJ#gGR$EVOXsKWlF}Y7+WAynT9WHS zt^Tkl5b92JIJp|F79RK!mrCGZhknAvc4}k9{7f|1rERBfP*WoR9Te@Mc1O(ht>N%` zZzvG$jJH~izhgpJ>2rOL9vYFF&=_+};Raf015?;SWAy734wK*x{fzHq>K}bHQwMN9 zZ}|)59DTfKLNk0{y40VRk$h)tgcz4)KDW$~$}_>iTa1!#hFB$HDbtg7LzXVjrucrB zv?xtH10wFCkBYx#4n}yze5yuj%g~V^TjQH}aM_9!?yZwrGUzQyP*(Z%jZ$9TY`fWF Pwv=~h+e-SI<?4R{6T4^| delta 3948 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 bTy1SlRDr*>J-s^zyLN2a+#?kCNXh>Iz|j56 diff --git a/src/Components/Web.JS/dist/Release/blazor.webassembly.js b/src/Components/Web.JS/dist/Release/blazor.webassembly.js index cd99a3089a22c368fc35f4e72cab4227fa267207..c335f6c5649a4aa07b6637179fcc2199e5aa1049 100644 GIT binary patch delta 8528 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 E|08&qQUCw| delta 10498 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 Zbk!S?!!g>ViK(;yevrvglU;o1zX9w}I3xf7 diff --git a/src/Components/Web.JS/src/Boot.Server.ts b/src/Components/Web.JS/src/Boot.Server.ts index c781d52aacc..ed5647dd128 100644 --- a/src/Components/Web.JS/src/Boot.Server.ts +++ b/src/Components/Web.JS/src/Boot.Server.ts @@ -12,7 +12,6 @@ import { setEventDispatcher } from './Rendering/RendererEventDispatcher'; import { resolveOptions, CircuitStartOptions } from './Platform/Circuits/CircuitStartOptions'; import { DefaultReconnectionHandler } from './Platform/Circuits/DefaultReconnectionHandler'; import { attachRootComponentToLogicalElement } from './Rendering/Renderer'; -import { initializeProfiling } from './Platform/Profiling'; let renderingFailed = false; let started = false; @@ -22,7 +21,6 @@ async function boot(userOptions?: Partial<CircuitStartOptions>): Promise<void> { throw new Error('Blazor has already started.'); } started = true; - initializeProfiling(null); // Establish options to be used const options = resolveOptions(userOptions); diff --git a/src/Components/Web.JS/src/Boot.WebAssembly.ts b/src/Components/Web.JS/src/Boot.WebAssembly.ts index 99b117ad78d..b0a08fe2b4b 100644 --- a/src/Components/Web.JS/src/Boot.WebAssembly.ts +++ b/src/Components/Web.JS/src/Boot.WebAssembly.ts @@ -11,7 +11,6 @@ import { WebAssemblyConfigLoader } from './Platform/WebAssemblyConfigLoader'; import { BootConfigResult } from './Platform/BootConfig'; import { Pointer } from './Platform/Platform'; import { WebAssemblyStartOptions } from './Platform/WebAssemblyStartOptions'; -import { profileStart, profileEnd } from './Platform/Profiling'; let started = false; @@ -34,8 +33,6 @@ async function boot(options?: Partial<WebAssemblyStartOptions>): Promise<void> { const platform = Environment.setPlatform(monoPlatform); window['Blazor'].platform = platform; 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 // 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 @@ -47,8 +44,6 @@ async function boot(options?: Partial<WebAssemblyStartOptions>): Promise<void> { } finally { heapLock.release(); } - - profileEnd('renderBatch'); }; // Configure navigation via JS Interop diff --git a/src/Components/Web.JS/src/GlobalExports.ts b/src/Components/Web.JS/src/GlobalExports.ts index 08f7557ba51..df7f9b18a84 100644 --- a/src/Components/Web.JS/src/GlobalExports.ts +++ b/src/Components/Web.JS/src/GlobalExports.ts @@ -1,7 +1,6 @@ import { navigateTo, internalFunctions as navigationManagerInternalFunctions } from './Services/NavigationManager'; import { attachRootComponentToElement } from './Rendering/Renderer'; import { domFunctions } from './DomWrapper'; -import { setProfilingEnabled } from './Platform/Profiling'; // Make the following APIs available in global scope for invocation from JS window['Blazor'] = { @@ -11,6 +10,5 @@ window['Blazor'] = { attachRootComponentToElement, navigationManager: navigationManagerInternalFunctions, domWrapper: domFunctions, - setProfilingEnabled: setProfilingEnabled, }, }; diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index d2abd2a70ed..5a5eda94224 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -5,7 +5,6 @@ import { WebAssemblyResourceLoader, LoadingResource } from '../WebAssemblyResour import { Platform, System_Array, Pointer, System_Object, System_String, HeapLock } from '../Platform'; import { loadTimezoneData } from './TimezoneDataFile'; import { WebAssemblyBootResourceType } from '../WebAssemblyStartOptions'; -import { initializeProfiling } from '../Profiling'; let mono_wasm_add_assembly: (name: string, heapAddress: number, length: number) => void; const appBinDirName = 'appBinDir'; @@ -36,10 +35,6 @@ export const monoPlatform: Platform = { start: function start(resourceLoader: WebAssemblyResourceLoader) { return new Promise<void>((resolve, reject) => { 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 window['Browser'] = { diff --git a/src/Components/Web.JS/src/Platform/Profiling.ts b/src/Components/Web.JS/src/Platform/Profiling.ts deleted file mode 100644 index 9c4f0f220b4..00000000000 --- a/src/Components/Web.JS/src/Platform/Profiling.ts +++ /dev/null @@ -1,134 +0,0 @@ -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; diff --git a/src/Components/Web.JS/src/Rendering/BrowserRenderer.ts b/src/Components/Web.JS/src/Rendering/BrowserRenderer.ts index 0c9c3ea2b96..14faf703291 100644 --- a/src/Components/Web.JS/src/Rendering/BrowserRenderer.ts +++ b/src/Components/Web.JS/src/Rendering/BrowserRenderer.ts @@ -6,7 +6,6 @@ import { applyCaptureIdToElement } from './ElementReferenceCapture'; import { EventFieldInfo } from './EventFieldInfo'; import { dispatchEvent } from './RendererEventDispatcher'; import { attachToEventDelegator as attachNavigationManagerToEventDelegator } from '../Services/NavigationManager'; -import { profileEnd, profileStart } from '../Platform/Profiling'; const selectValuePropname = '_blazorSelectValue'; const sharedTemplateElemForParsing = document.createElement('template'); 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 { - profileStart('updateComponent'); - const element = this.childComponentLocations[componentId]; if (!element) { 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) { activeElementBefore.focus(); } - - profileEnd('updateComponent'); } public disposeComponent(componentId: number) { diff --git a/src/Components/Shared/src/WebAssemblyJSInteropInternalCalls.cs b/src/Components/WebAssembly/JSInterop/src/InternalCalls.cs similarity index 100% rename from src/Components/Shared/src/WebAssemblyJSInteropInternalCalls.cs rename to src/Components/WebAssembly/JSInterop/src/InternalCalls.cs diff --git a/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj b/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj index 64d0f52e108..8b58eb02dd6 100644 --- a/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj +++ b/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj @@ -9,7 +9,6 @@ </PropertyGroup> <ItemGroup> - <Compile Include="$(ComponentsSharedSourceRoot)src\WebAssemblyJSInteropInternalCalls.cs" /> <Reference Include="Microsoft.JSInterop" /> </ItemGroup> -- GitLab