Improve performance of Span.Fill (dotnet/coreclr#10084)
authorJan Kotas <jkotas@microsoft.com>
Fri, 10 Mar 2017 21:43:37 +0000 (13:43 -0800)
committerGitHub <noreply@github.com>
Fri, 10 Mar 2017 21:43:37 +0000 (13:43 -0800)
Commit migrated from https://github.com/dotnet/coreclr/commit/bb1c085dca8a7fab54b2860fc9e42ea49cf50ae5

src/coreclr/src/mscorlib/src/System/Span.cs

index 449293c..c860046 100644 (file)
@@ -224,47 +224,50 @@ namespace System
         /// </summary>
         public void Fill(T value)
         {
-            int length = _length;
-
-            if (length == 0)
-                return;
-
             if (Unsafe.SizeOf<T>() == 1)
             {
-                byte fill = Unsafe.As<T, byte>(ref value);
-                ref byte r = ref Unsafe.As<T, byte>(ref _pointer.Value);
-                Unsafe.InitBlockUnaligned(ref r, fill, (uint)length);
+                uint length = (uint)_length;
+                if (length == 0)
+                    return;
+
+                T tmp = value; // Avoid taking address of the "value" argument. It would regress performance of the loop below.
+                Unsafe.InitBlockUnaligned(ref Unsafe.As<T, byte>(ref _pointer.Value), Unsafe.As<T, byte>(ref tmp), length);
             }
             else
             {
+                // Do all math as nuint to avoid unnecessary 64->32->64 bit integer truncations
+                nuint length = (uint)_length;
+                if (length == 0)
+                    return;
+
                 ref T r = ref DangerousGetPinnableReference();
 
                 // TODO: Create block fill for value types of power of two sizes e.g. 2,4,8,16
 
-                // Simple loop unrolling
-                int i = 0;
-                for (; i < (length & ~7); i += 8)
+                nuint elementSize = (uint)Unsafe.SizeOf<T>();
+                nuint i = 0;
+                for (; i < (length & ~(nuint)7); i += 8)
                 {
-                    Unsafe.Add<T>(ref r, i + 0) = value;
-                    Unsafe.Add<T>(ref r, i + 1) = value;
-                    Unsafe.Add<T>(ref r, i + 2) = value;
-                    Unsafe.Add<T>(ref r, i + 3) = value;
-                    Unsafe.Add<T>(ref r, i + 4) = value;
-                    Unsafe.Add<T>(ref r, i + 5) = value;
-                    Unsafe.Add<T>(ref r, i + 6) = value;
-                    Unsafe.Add<T>(ref r, i + 7) = value;
+                    Unsafe.AddByteOffset<T>(ref r, (i + 0) * elementSize) = value;
+                    Unsafe.AddByteOffset<T>(ref r, (i + 1) * elementSize) = value;
+                    Unsafe.AddByteOffset<T>(ref r, (i + 2) * elementSize) = value;
+                    Unsafe.AddByteOffset<T>(ref r, (i + 3) * elementSize) = value;
+                    Unsafe.AddByteOffset<T>(ref r, (i + 4) * elementSize) = value;
+                    Unsafe.AddByteOffset<T>(ref r, (i + 5) * elementSize) = value;
+                    Unsafe.AddByteOffset<T>(ref r, (i + 6) * elementSize) = value;
+                    Unsafe.AddByteOffset<T>(ref r, (i + 7) * elementSize) = value;
                 }
-                if (i < (length & ~3))
+                if (i < (length & ~(nuint)3))
                 {
-                    Unsafe.Add<T>(ref r, i + 0) = value;
-                    Unsafe.Add<T>(ref r, i + 1) = value;
-                    Unsafe.Add<T>(ref r, i + 2) = value;
-                    Unsafe.Add<T>(ref r, i + 3) = value;
+                    Unsafe.AddByteOffset<T>(ref r, (i + 0) * elementSize) = value;
+                    Unsafe.AddByteOffset<T>(ref r, (i + 1) * elementSize) = value;
+                    Unsafe.AddByteOffset<T>(ref r, (i + 2) * elementSize) = value;
+                    Unsafe.AddByteOffset<T>(ref r, (i + 3) * elementSize) = value;
                     i += 4;
                 }
                 for (; i < length; i++)
                 {
-                    Unsafe.Add<T>(ref r, i) = value;
+                    Unsafe.AddByteOffset<T>(ref r, i * elementSize) = value;
                 }
             }
         }