Browse Source

Faster iteration in TagHelpersRunner (#27975)

Ben Adams 5 years ago
parent
commit
6379d16d9d

+ 5 - 15
src/Razor/Razor.Runtime/src/Runtime/TagHelpers/TagHelperExecutionContext.cs

@@ -88,13 +88,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
         /// <summary>
         /// Indicates if <see cref="GetChildContentAsync"/> has been called.
         /// </summary>
-        public bool ChildContentRetrieved
-        {
-            get
-            {
-                return _childContent != null;
-            }
-        }
+        public bool ChildContentRetrieved => _childContent != null;
 
         /// <summary>
         /// Gets the collection of items used to communicate with other <see cref="ITagHelper"/>s.
@@ -104,13 +98,9 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
         /// <summary>
         /// <see cref="ITagHelper"/>s that should be run.
         /// </summary>
-        public IList<ITagHelper> TagHelpers
-        {
-            get
-            {
-                return _tagHelpers;
-            }
-        }
+        public IList<ITagHelper> TagHelpers => _tagHelpers;
+
+        internal List<ITagHelper> TagHelperList => _tagHelpers;
 
         // Internal set for testing.
         /// <summary>
@@ -320,4 +310,4 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
             return new DefaultTagHelperContent().SetHtmlContent(childContent);
         }
     }
-}
+}

+ 12 - 27
src/Razor/Razor.Runtime/src/Runtime/TagHelpers/TagHelperRunner.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Runtime.InteropServices;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Razor.TagHelpers;
 
@@ -28,24 +29,23 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
             }
 
             var tagHelperContext = executionContext.Context;
-            var tagHelpers = executionContext.TagHelpers;
-            OrderTagHelpers(tagHelpers);
+            var tagHelpers = CollectionsMarshal.AsSpan(executionContext.TagHelperList);
 
-            // Read interface .Count once rather than per iteration
-            var count = tagHelpers.Count;
-            for (var i = 0; i < count; i++)
+            tagHelpers.Sort(default(SortTagHelpers));
+
+            foreach (var tagHelper in tagHelpers)
             {
-                tagHelpers[i].Init(tagHelperContext);
+                tagHelper.Init(tagHelperContext);
             }
 
             var tagHelperOutput = executionContext.Output;
 
-            for (var i = 0; i < count; i++)
+            for (var i = 0; i < tagHelpers.Length; i++)
             {
                 var task = tagHelpers[i].ProcessAsync(tagHelperContext, tagHelperOutput);
                 if (!task.IsCompletedSuccessfully)
                 {
-                    return Awaited(task, executionContext, i + 1, count);
+                    return Awaited(task, executionContext, i + 1, tagHelpers.Length);
                 }
             }
 
@@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
             {
                 await task;
 
-                var tagHelpers = executionContext.TagHelpers;
+                var tagHelpers = executionContext.TagHelperList;
                 var tagHelperOutput = executionContext.Output;
                 var tagHelperContext = executionContext.Context;
                 for (; i < count; i++)
@@ -65,25 +65,10 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
             }
         }
 
-        private static void OrderTagHelpers(IList<ITagHelper> tagHelpers)
+        private readonly struct SortTagHelpers : IComparer<ITagHelper>
         {
-            // Using bubble-sort here due to its simplicity. It'd be an extreme corner case to ever have more than 3 or
-            // 4 tag helpers simultaneously.
-
-            // Read interface .Count once rather than per iteration
-            var count = tagHelpers.Count;
-            for (var i = 0; i < count; i++)
-            {
-                for (var j = i + 1; j < count; j++)
-                {
-                    if (tagHelpers[j].Order < tagHelpers[i].Order)
-                    {
-                        var temp = tagHelpers[i];
-                        tagHelpers[i] = tagHelpers[j];
-                        tagHelpers[j] = temp;
-                    }
-                }
-            }
+            public int Compare(ITagHelper left, ITagHelper right)
+                => left.Order.CompareTo(right.Order);
         }
     }
 }