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