From 9a490690ab5e28c21c35b81810a984fee7bb2059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Wed, 16 Jan 2019 20:04:29 -0500 Subject: [PATCH 1/6] Replace unsafe code with Span --- .../Http/src/Features/HttpRequestIdentifierFeature.cs | 6 +++--- src/Middleware/ResponseCaching/src/Internal/FastGuid.cs | 8 ++++---- .../src/Internal/Infrastructure/CorrelationIdGenerator.cs | 6 +++--- .../Core/src/Internal/Infrastructure/StringUtilities.cs | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs b/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs index 34663937a5a5..bce72ab90267 100644 --- a/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs +++ b/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs @@ -34,14 +34,14 @@ public string TraceIdentifier } } - private static unsafe string GenerateRequestId(long id) + private static string GenerateRequestId(long id) { // The following routine is ~310% faster than calling long.ToString() on x64 // and ~600% faster than calling long.ToString() on x86 in tight loops of 1 million+ iterations // See: https://github.com/aspnet/Hosting/pull/385 // stackalloc to allocate array on stack rather than heap - char* charBuffer = stackalloc char[13]; + Span charBuffer = stackalloc char[13]; charBuffer[0] = _encode32Chars[(int)(id >> 60) & 31]; charBuffer[1] = _encode32Chars[(int)(id >> 55) & 31]; @@ -58,7 +58,7 @@ private static unsafe string GenerateRequestId(long id) charBuffer[12] = _encode32Chars[(int)id & 31]; // string ctor overload that takes char* - return new string(charBuffer, 0, 13); + return new string(charBuffer); } } } diff --git a/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs b/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs index b62ba22f8388..911ae488ae09 100644 --- a/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs +++ b/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -53,10 +53,10 @@ internal static FastGuid NewGuid() return new FastGuid(Interlocked.Increment(ref NextId)); } - private static unsafe string GenerateGuidString(FastGuid guid) + private static string GenerateGuidString(FastGuid guid) { // stackalloc to allocate array on stack rather than heap - char* charBuffer = stackalloc char[13]; + Span charBuffer = stackalloc char[13]; // ID charBuffer[0] = _encode32Chars[(int)(guid.IdValue >> 60) & 31]; @@ -74,7 +74,7 @@ private static unsafe string GenerateGuidString(FastGuid guid) charBuffer[12] = _encode32Chars[(int)guid.IdValue & 31]; // string ctor overload that takes char* - return new string(charBuffer, 0, 13); + return new string(charBuffer); } } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs index fc161d4116a6..d13f384ab0f1 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs @@ -18,14 +18,14 @@ internal static class CorrelationIdGenerator public static string GetNextId() => GenerateId(Interlocked.Increment(ref _lastId)); - private static unsafe string GenerateId(long id) + private static string GenerateId(long id) { // The following routine is ~310% faster than calling long.ToString() on x64 // and ~600% faster than calling long.ToString() on x86 in tight loops of 1 million+ iterations // See: https://github.com/aspnet/Hosting/pull/385 // stackalloc to allocate array on stack rather than heap - char* charBuffer = stackalloc char[13]; + Span charBuffer = stackalloc char[13]; charBuffer[0] = _encode32Chars[(int)(id >> 60) & 31]; charBuffer[1] = _encode32Chars[(int)(id >> 55) & 31]; @@ -42,7 +42,7 @@ private static unsafe string GenerateId(long id) charBuffer[12] = _encode32Chars[(int)id & 31]; // string ctor overload that takes char* - return new string(charBuffer, 0, 13); + return new string(charBuffer); } } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs index 95073a97de11..961ee756d859 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs @@ -118,7 +118,7 @@ public static unsafe bool TryGetAsciiString(byte* input, char* output, int count /// /// /// - public static unsafe string ConcatAsHexSuffix(string str, char separator, uint number) + public static string ConcatAsHexSuffix(string str, char separator, uint number) { var length = 1 + 8; if (str != null) @@ -127,7 +127,7 @@ public static unsafe string ConcatAsHexSuffix(string str, char separator, uint n } // stackalloc to allocate array on stack rather than heap - char* charBuffer = stackalloc char[length]; + Span charBuffer = stackalloc char[length]; var i = 0; if (str != null) @@ -150,7 +150,7 @@ public static unsafe string ConcatAsHexSuffix(string str, char separator, uint n charBuffer[i + 8] = _encode16Chars[(int)number & 0xF]; // string ctor overload that takes char* - return new string(charBuffer, 0, length); + return new string(charBuffer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] // Needs a push From 680c0a1371f04278377ffc7edeea6fcd6d2be00f Mon Sep 17 00:00:00 2001 From: meziantou Date: Thu, 17 Jan 2019 13:42:21 -0500 Subject: [PATCH 2/6] use string.Create --- .../Features/HttpRequestIdentifierFeature.cs | 38 +++++++++---------- .../ResponseCaching/src/Internal/FastGuid.cs | 36 ++++++++---------- .../Infrastructure/CorrelationIdGenerator.cs | 35 ++++++++--------- 3 files changed, 50 insertions(+), 59 deletions(-) diff --git a/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs b/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs index bce72ab90267..9c7395982f5c 100644 --- a/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs +++ b/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs @@ -8,9 +8,9 @@ namespace Microsoft.AspNetCore.Http.Features { public class HttpRequestIdentifierFeature : IHttpRequestIdentifierFeature { - // Base32 encoding - in ascii sort order for easy text based sorting + // Base32 encoding - in ascii sort order for easy text based sorting private static readonly string _encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; - // Seed the _requestId for this application instance with + // Seed the _requestId for this application instance with // the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001 // for a roughly increasing _requestId over restarts private static long _requestId = DateTime.UtcNow.Ticks; @@ -41,24 +41,22 @@ private static string GenerateRequestId(long id) // See: https://github.com/aspnet/Hosting/pull/385 // stackalloc to allocate array on stack rather than heap - Span charBuffer = stackalloc char[13]; - - charBuffer[0] = _encode32Chars[(int)(id >> 60) & 31]; - charBuffer[1] = _encode32Chars[(int)(id >> 55) & 31]; - charBuffer[2] = _encode32Chars[(int)(id >> 50) & 31]; - charBuffer[3] = _encode32Chars[(int)(id >> 45) & 31]; - charBuffer[4] = _encode32Chars[(int)(id >> 40) & 31]; - charBuffer[5] = _encode32Chars[(int)(id >> 35) & 31]; - charBuffer[6] = _encode32Chars[(int)(id >> 30) & 31]; - charBuffer[7] = _encode32Chars[(int)(id >> 25) & 31]; - charBuffer[8] = _encode32Chars[(int)(id >> 20) & 31]; - charBuffer[9] = _encode32Chars[(int)(id >> 15) & 31]; - charBuffer[10] = _encode32Chars[(int)(id >> 10) & 31]; - charBuffer[11] = _encode32Chars[(int)(id >> 5) & 31]; - charBuffer[12] = _encode32Chars[(int)id & 31]; - - // string ctor overload that takes char* - return new string(charBuffer); + return string.Create(13, id, (span, value) => + { + span[0] = _encode32Chars[(int)(value >> 60) & 31]; + span[1] = _encode32Chars[(int)(value >> 55) & 31]; + span[2] = _encode32Chars[(int)(value >> 50) & 31]; + span[3] = _encode32Chars[(int)(value >> 45) & 31]; + span[4] = _encode32Chars[(int)(value >> 40) & 31]; + span[5] = _encode32Chars[(int)(value >> 35) & 31]; + span[6] = _encode32Chars[(int)(value >> 30) & 31]; + span[7] = _encode32Chars[(int)(value >> 25) & 31]; + span[8] = _encode32Chars[(int)(value >> 20) & 31]; + span[9] = _encode32Chars[(int)(value >> 15) & 31]; + span[10] = _encode32Chars[(int)(value >> 10) & 31]; + span[11] = _encode32Chars[(int)(value >> 5) & 31]; + span[12] = _encode32Chars[(int)value & 31]; + }); } } } diff --git a/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs b/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs index 911ae488ae09..e8295d3b135d 100644 --- a/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs +++ b/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs @@ -55,26 +55,22 @@ internal static FastGuid NewGuid() private static string GenerateGuidString(FastGuid guid) { - // stackalloc to allocate array on stack rather than heap - Span charBuffer = stackalloc char[13]; - - // ID - charBuffer[0] = _encode32Chars[(int)(guid.IdValue >> 60) & 31]; - charBuffer[1] = _encode32Chars[(int)(guid.IdValue >> 55) & 31]; - charBuffer[2] = _encode32Chars[(int)(guid.IdValue >> 50) & 31]; - charBuffer[3] = _encode32Chars[(int)(guid.IdValue >> 45) & 31]; - charBuffer[4] = _encode32Chars[(int)(guid.IdValue >> 40) & 31]; - charBuffer[5] = _encode32Chars[(int)(guid.IdValue >> 35) & 31]; - charBuffer[6] = _encode32Chars[(int)(guid.IdValue >> 30) & 31]; - charBuffer[7] = _encode32Chars[(int)(guid.IdValue >> 25) & 31]; - charBuffer[8] = _encode32Chars[(int)(guid.IdValue >> 20) & 31]; - charBuffer[9] = _encode32Chars[(int)(guid.IdValue >> 15) & 31]; - charBuffer[10] = _encode32Chars[(int)(guid.IdValue >> 10) & 31]; - charBuffer[11] = _encode32Chars[(int)(guid.IdValue >> 5) & 31]; - charBuffer[12] = _encode32Chars[(int)guid.IdValue & 31]; - - // string ctor overload that takes char* - return new string(charBuffer); + return string.Create(13, guid.IdValue, (span, value) => + { + span[0] = _encode32Chars[(int)(value >> 60) & 31]; + span[1] = _encode32Chars[(int)(value >> 55) & 31]; + span[2] = _encode32Chars[(int)(value >> 50) & 31]; + span[3] = _encode32Chars[(int)(value >> 45) & 31]; + span[4] = _encode32Chars[(int)(value >> 40) & 31]; + span[5] = _encode32Chars[(int)(value >> 35) & 31]; + span[6] = _encode32Chars[(int)(value >> 30) & 31]; + span[7] = _encode32Chars[(int)(value >> 25) & 31]; + span[8] = _encode32Chars[(int)(value >> 20) & 31]; + span[9] = _encode32Chars[(int)(value >> 15) & 31]; + span[10] = _encode32Chars[(int)(value >> 10) & 31]; + span[11] = _encode32Chars[(int)(value >> 5) & 31]; + span[12] = _encode32Chars[(int)value & 31]; + }); } } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs index d13f384ab0f1..86636594d379 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs @@ -24,25 +24,22 @@ private static string GenerateId(long id) // and ~600% faster than calling long.ToString() on x86 in tight loops of 1 million+ iterations // See: https://github.com/aspnet/Hosting/pull/385 - // stackalloc to allocate array on stack rather than heap - Span charBuffer = stackalloc char[13]; - - charBuffer[0] = _encode32Chars[(int)(id >> 60) & 31]; - charBuffer[1] = _encode32Chars[(int)(id >> 55) & 31]; - charBuffer[2] = _encode32Chars[(int)(id >> 50) & 31]; - charBuffer[3] = _encode32Chars[(int)(id >> 45) & 31]; - charBuffer[4] = _encode32Chars[(int)(id >> 40) & 31]; - charBuffer[5] = _encode32Chars[(int)(id >> 35) & 31]; - charBuffer[6] = _encode32Chars[(int)(id >> 30) & 31]; - charBuffer[7] = _encode32Chars[(int)(id >> 25) & 31]; - charBuffer[8] = _encode32Chars[(int)(id >> 20) & 31]; - charBuffer[9] = _encode32Chars[(int)(id >> 15) & 31]; - charBuffer[10] = _encode32Chars[(int)(id >> 10) & 31]; - charBuffer[11] = _encode32Chars[(int)(id >> 5) & 31]; - charBuffer[12] = _encode32Chars[(int)id & 31]; - - // string ctor overload that takes char* - return new string(charBuffer); + return string.Create(13, id, (span, value) => + { + span[0] = _encode32Chars[(int)(value >> 60) & 31]; + span[1] = _encode32Chars[(int)(value >> 55) & 31]; + span[2] = _encode32Chars[(int)(value >> 50) & 31]; + span[3] = _encode32Chars[(int)(value >> 45) & 31]; + span[4] = _encode32Chars[(int)(value >> 40) & 31]; + span[5] = _encode32Chars[(int)(value >> 35) & 31]; + span[6] = _encode32Chars[(int)(value >> 30) & 31]; + span[7] = _encode32Chars[(int)(value >> 25) & 31]; + span[8] = _encode32Chars[(int)(value >> 20) & 31]; + span[9] = _encode32Chars[(int)(value >> 15) & 31]; + span[10] = _encode32Chars[(int)(value >> 10) & 31]; + span[11] = _encode32Chars[(int)(value >> 5) & 31]; + span[12] = _encode32Chars[(int)value & 31]; + }); } } } From 8f6024033ebc1dad343bd3552a908356095a4330 Mon Sep 17 00:00:00 2001 From: meziantou Date: Thu, 17 Jan 2019 14:41:10 -0500 Subject: [PATCH 3/6] Use string.Create --- .../Infrastructure/StringUtilities.cs | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs index 961ee756d859..5b5ced502225 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs @@ -94,8 +94,8 @@ public static unsafe bool TryGetAsciiString(byte* input, char* output, int count var vector = Unsafe.AsRef>(input); isValid &= CheckBytesInAsciiRange(vector); Vector.Widen( - vector, - out Unsafe.AsRef>(output), + vector, + out Unsafe.AsRef>(output), out Unsafe.AsRef>(output + Vector.Count)); input += Vector.Count; @@ -126,31 +126,28 @@ public static string ConcatAsHexSuffix(string str, char separator, uint number) length += str.Length; } - // stackalloc to allocate array on stack rather than heap - Span charBuffer = stackalloc char[length]; - - var i = 0; - if (str != null) + return string.Create(length, (str, separator, number), (charBuffer, tuple) => { - for (i = 0; i < str.Length; i++) + var i = 0; + if (tuple.str != null) { - charBuffer[i] = str[i]; + for (i = 0; i < tuple.str.Length; i++) + { + charBuffer[i] = tuple.str[i]; + } } - } - - charBuffer[i] = separator; - - charBuffer[i + 1] = _encode16Chars[(int)(number >> 28) & 0xF]; - charBuffer[i + 2] = _encode16Chars[(int)(number >> 24) & 0xF]; - charBuffer[i + 3] = _encode16Chars[(int)(number >> 20) & 0xF]; - charBuffer[i + 4] = _encode16Chars[(int)(number >> 16) & 0xF]; - charBuffer[i + 5] = _encode16Chars[(int)(number >> 12) & 0xF]; - charBuffer[i + 6] = _encode16Chars[(int)(number >> 8) & 0xF]; - charBuffer[i + 7] = _encode16Chars[(int)(number >> 4) & 0xF]; - charBuffer[i + 8] = _encode16Chars[(int)number & 0xF]; - // string ctor overload that takes char* - return new string(charBuffer); + charBuffer[i] = tuple.separator; + + charBuffer[i + 1] = _encode16Chars[(int)(tuple.number >> 28) & 0xF]; + charBuffer[i + 2] = _encode16Chars[(int)(tuple.number >> 24) & 0xF]; + charBuffer[i + 3] = _encode16Chars[(int)(tuple.number >> 20) & 0xF]; + charBuffer[i + 4] = _encode16Chars[(int)(tuple.number >> 16) & 0xF]; + charBuffer[i + 5] = _encode16Chars[(int)(tuple.number >> 12) & 0xF]; + charBuffer[i + 6] = _encode16Chars[(int)(tuple.number >> 8) & 0xF]; + charBuffer[i + 7] = _encode16Chars[(int)(tuple.number >> 4) & 0xF]; + charBuffer[i + 8] = _encode16Chars[(int)tuple.number & 0xF]; + }); } [MethodImpl(MethodImplOptions.AggressiveInlining)] // Needs a push From a8329590d2b271bf629416d98f04f5c47914c0e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Thu, 17 Jan 2019 20:10:08 -0500 Subject: [PATCH 4/6] Reverse order --- .../Infrastructure/StringUtilities.cs | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs index 5b5ced502225..93189a1f89a1 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -136,17 +136,16 @@ public static string ConcatAsHexSuffix(string str, char separator, uint number) charBuffer[i] = tuple.str[i]; } } - - charBuffer[i] = tuple.separator; - - charBuffer[i + 1] = _encode16Chars[(int)(tuple.number >> 28) & 0xF]; - charBuffer[i + 2] = _encode16Chars[(int)(tuple.number >> 24) & 0xF]; - charBuffer[i + 3] = _encode16Chars[(int)(tuple.number >> 20) & 0xF]; - charBuffer[i + 4] = _encode16Chars[(int)(tuple.number >> 16) & 0xF]; - charBuffer[i + 5] = _encode16Chars[(int)(tuple.number >> 12) & 0xF]; - charBuffer[i + 6] = _encode16Chars[(int)(tuple.number >> 8) & 0xF]; - charBuffer[i + 7] = _encode16Chars[(int)(tuple.number >> 4) & 0xF]; + charBuffer[i + 8] = _encode16Chars[(int)tuple.number & 0xF]; + charBuffer[i + 7] = _encode16Chars[(int)(tuple.number >> 4) & 0xF]; + charBuffer[i + 6] = _encode16Chars[(int)(tuple.number >> 8) & 0xF]; + charBuffer[i + 5] = _encode16Chars[(int)(tuple.number >> 12) & 0xF]; + charBuffer[i + 4] = _encode16Chars[(int)(tuple.number >> 16) & 0xF]; + charBuffer[i + 3] = _encode16Chars[(int)(tuple.number >> 20) & 0xF]; + charBuffer[i + 2] = _encode16Chars[(int)(tuple.number >> 24) & 0xF]; + charBuffer[i + 1] = _encode16Chars[(int)(tuple.number >> 28) & 0xF]; + charBuffer[i] = tuple.separator; }); } From ec63f464eabca7011f1d1c6cf871646d74f1be43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Thu, 17 Jan 2019 21:46:11 -0500 Subject: [PATCH 5/6] Optimization to avoid bounds checks --- .../Features/HttpRequestIdentifierFeature.cs | 24 +++++++++---------- .../ResponseCaching/src/Internal/FastGuid.cs | 24 +++++++++---------- .../Infrastructure/CorrelationIdGenerator.cs | 24 +++++++++---------- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs b/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs index 9c7395982f5c..b10d3f71bb04 100644 --- a/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs +++ b/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs @@ -43,19 +43,19 @@ private static string GenerateRequestId(long id) // stackalloc to allocate array on stack rather than heap return string.Create(13, id, (span, value) => { - span[0] = _encode32Chars[(int)(value >> 60) & 31]; - span[1] = _encode32Chars[(int)(value >> 55) & 31]; - span[2] = _encode32Chars[(int)(value >> 50) & 31]; - span[3] = _encode32Chars[(int)(value >> 45) & 31]; - span[4] = _encode32Chars[(int)(value >> 40) & 31]; - span[5] = _encode32Chars[(int)(value >> 35) & 31]; - span[6] = _encode32Chars[(int)(value >> 30) & 31]; - span[7] = _encode32Chars[(int)(value >> 25) & 31]; - span[8] = _encode32Chars[(int)(value >> 20) & 31]; - span[9] = _encode32Chars[(int)(value >> 15) & 31]; - span[10] = _encode32Chars[(int)(value >> 10) & 31]; - span[11] = _encode32Chars[(int)(value >> 5) & 31]; span[12] = _encode32Chars[(int)value & 31]; + span[11] = _encode32Chars[(int)(value >> 5) & 31]; + span[10] = _encode32Chars[(int)(value >> 10) & 31]; + span[9] = _encode32Chars[(int)(value >> 15) & 31]; + span[8] = _encode32Chars[(int)(value >> 20) & 31]; + span[7] = _encode32Chars[(int)(value >> 25) & 31]; + span[6] = _encode32Chars[(int)(value >> 30) & 31]; + span[5] = _encode32Chars[(int)(value >> 35) & 31]; + span[4] = _encode32Chars[(int)(value >> 40) & 31]; + span[3] = _encode32Chars[(int)(value >> 45) & 31]; + span[2] = _encode32Chars[(int)(value >> 50) & 31]; + span[1] = _encode32Chars[(int)(value >> 55) & 31]; + span[0] = _encode32Chars[(int)(value >> 60) & 31]; }); } } diff --git a/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs b/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs index e8295d3b135d..fc01a1b031ff 100644 --- a/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs +++ b/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs @@ -57,19 +57,19 @@ private static string GenerateGuidString(FastGuid guid) { return string.Create(13, guid.IdValue, (span, value) => { - span[0] = _encode32Chars[(int)(value >> 60) & 31]; - span[1] = _encode32Chars[(int)(value >> 55) & 31]; - span[2] = _encode32Chars[(int)(value >> 50) & 31]; - span[3] = _encode32Chars[(int)(value >> 45) & 31]; - span[4] = _encode32Chars[(int)(value >> 40) & 31]; - span[5] = _encode32Chars[(int)(value >> 35) & 31]; - span[6] = _encode32Chars[(int)(value >> 30) & 31]; - span[7] = _encode32Chars[(int)(value >> 25) & 31]; - span[8] = _encode32Chars[(int)(value >> 20) & 31]; - span[9] = _encode32Chars[(int)(value >> 15) & 31]; - span[10] = _encode32Chars[(int)(value >> 10) & 31]; - span[11] = _encode32Chars[(int)(value >> 5) & 31]; span[12] = _encode32Chars[(int)value & 31]; + span[11] = _encode32Chars[(int)(value >> 5) & 31]; + span[10] = _encode32Chars[(int)(value >> 10) & 31]; + span[9] = _encode32Chars[(int)(value >> 15) & 31]; + span[8] = _encode32Chars[(int)(value >> 20) & 31]; + span[7] = _encode32Chars[(int)(value >> 25) & 31]; + span[6] = _encode32Chars[(int)(value >> 30) & 31]; + span[5] = _encode32Chars[(int)(value >> 35) & 31]; + span[4] = _encode32Chars[(int)(value >> 40) & 31]; + span[3] = _encode32Chars[(int)(value >> 45) & 31]; + span[2] = _encode32Chars[(int)(value >> 50) & 31]; + span[1] = _encode32Chars[(int)(value >> 55) & 31]; + span[0] = _encode32Chars[(int)(value >> 60) & 31]; }); } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs index 86636594d379..6d3e8647d7c3 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs @@ -26,19 +26,19 @@ private static string GenerateId(long id) return string.Create(13, id, (span, value) => { - span[0] = _encode32Chars[(int)(value >> 60) & 31]; - span[1] = _encode32Chars[(int)(value >> 55) & 31]; - span[2] = _encode32Chars[(int)(value >> 50) & 31]; - span[3] = _encode32Chars[(int)(value >> 45) & 31]; - span[4] = _encode32Chars[(int)(value >> 40) & 31]; - span[5] = _encode32Chars[(int)(value >> 35) & 31]; - span[6] = _encode32Chars[(int)(value >> 30) & 31]; - span[7] = _encode32Chars[(int)(value >> 25) & 31]; - span[8] = _encode32Chars[(int)(value >> 20) & 31]; - span[9] = _encode32Chars[(int)(value >> 15) & 31]; - span[10] = _encode32Chars[(int)(value >> 10) & 31]; - span[11] = _encode32Chars[(int)(value >> 5) & 31]; span[12] = _encode32Chars[(int)value & 31]; + span[11] = _encode32Chars[(int)(value >> 5) & 31]; + span[10] = _encode32Chars[(int)(value >> 10) & 31]; + span[9] = _encode32Chars[(int)(value >> 15) & 31]; + span[8] = _encode32Chars[(int)(value >> 20) & 31]; + span[7] = _encode32Chars[(int)(value >> 25) & 31]; + span[6] = _encode32Chars[(int)(value >> 30) & 31]; + span[5] = _encode32Chars[(int)(value >> 35) & 31]; + span[4] = _encode32Chars[(int)(value >> 40) & 31]; + span[3] = _encode32Chars[(int)(value >> 45) & 31]; + span[2] = _encode32Chars[(int)(value >> 50) & 31]; + span[1] = _encode32Chars[(int)(value >> 55) & 31]; + span[0] = _encode32Chars[(int)(value >> 60) & 31]; }); } } From a528701aa3b293729911445896c6e33b68dc4d7a Mon Sep 17 00:00:00 2001 From: meziantou Date: Fri, 18 Jan 2019 09:54:00 -0500 Subject: [PATCH 6/6] Remove explicit int cast and use a local variable for the buffer --- .../Features/HttpRequestIdentifierFeature.cs | 37 +++++++++---------- .../ResponseCaching/src/Internal/FastGuid.cs | 31 ++++++++-------- .../Infrastructure/CorrelationIdGenerator.cs | 36 +++++++++--------- .../Infrastructure/StringUtilities.cs | 35 +++++++++--------- 4 files changed, 68 insertions(+), 71 deletions(-) diff --git a/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs b/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs index b10d3f71bb04..dd4cde7bf873 100644 --- a/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs +++ b/src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Http.Features public class HttpRequestIdentifierFeature : IHttpRequestIdentifierFeature { // Base32 encoding - in ascii sort order for easy text based sorting - private static readonly string _encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; + private static readonly char[] s_encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV".ToCharArray(); // Seed the _requestId for this application instance with // the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001 // for a roughly increasing _requestId over restarts @@ -36,26 +36,23 @@ public string TraceIdentifier private static string GenerateRequestId(long id) { - // The following routine is ~310% faster than calling long.ToString() on x64 - // and ~600% faster than calling long.ToString() on x86 in tight loops of 1 million+ iterations - // See: https://github.com/aspnet/Hosting/pull/385 - - // stackalloc to allocate array on stack rather than heap - return string.Create(13, id, (span, value) => + return string.Create(13, id, (buffer, value) => { - span[12] = _encode32Chars[(int)value & 31]; - span[11] = _encode32Chars[(int)(value >> 5) & 31]; - span[10] = _encode32Chars[(int)(value >> 10) & 31]; - span[9] = _encode32Chars[(int)(value >> 15) & 31]; - span[8] = _encode32Chars[(int)(value >> 20) & 31]; - span[7] = _encode32Chars[(int)(value >> 25) & 31]; - span[6] = _encode32Chars[(int)(value >> 30) & 31]; - span[5] = _encode32Chars[(int)(value >> 35) & 31]; - span[4] = _encode32Chars[(int)(value >> 40) & 31]; - span[3] = _encode32Chars[(int)(value >> 45) & 31]; - span[2] = _encode32Chars[(int)(value >> 50) & 31]; - span[1] = _encode32Chars[(int)(value >> 55) & 31]; - span[0] = _encode32Chars[(int)(value >> 60) & 31]; + char[] encode32Chars = s_encode32Chars; + + buffer[12] = encode32Chars[value & 31]; + buffer[11] = encode32Chars[(value >> 5) & 31]; + buffer[10] = encode32Chars[(value >> 10) & 31]; + buffer[9] = encode32Chars[(value >> 15) & 31]; + buffer[8] = encode32Chars[(value >> 20) & 31]; + buffer[7] = encode32Chars[(value >> 25) & 31]; + buffer[6] = encode32Chars[(value >> 30) & 31]; + buffer[5] = encode32Chars[(value >> 35) & 31]; + buffer[4] = encode32Chars[(value >> 40) & 31]; + buffer[3] = encode32Chars[(value >> 45) & 31]; + buffer[2] = encode32Chars[(value >> 50) & 31]; + buffer[1] = encode32Chars[(value >> 55) & 31]; + buffer[0] = encode32Chars[(value >> 60) & 31]; }); } } diff --git a/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs b/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs index fc01a1b031ff..571227516b09 100644 --- a/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs +++ b/src/Middleware/ResponseCaching/src/Internal/FastGuid.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal internal class FastGuid { // Base32 encoding - in ascii sort order for easy text based sorting - private static readonly string _encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; + private static readonly char[] s_encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV".ToCharArray(); // Global ID private static long NextId; @@ -55,21 +55,22 @@ internal static FastGuid NewGuid() private static string GenerateGuidString(FastGuid guid) { - return string.Create(13, guid.IdValue, (span, value) => + return string.Create(13, guid.IdValue, (buffer, value) => { - span[12] = _encode32Chars[(int)value & 31]; - span[11] = _encode32Chars[(int)(value >> 5) & 31]; - span[10] = _encode32Chars[(int)(value >> 10) & 31]; - span[9] = _encode32Chars[(int)(value >> 15) & 31]; - span[8] = _encode32Chars[(int)(value >> 20) & 31]; - span[7] = _encode32Chars[(int)(value >> 25) & 31]; - span[6] = _encode32Chars[(int)(value >> 30) & 31]; - span[5] = _encode32Chars[(int)(value >> 35) & 31]; - span[4] = _encode32Chars[(int)(value >> 40) & 31]; - span[3] = _encode32Chars[(int)(value >> 45) & 31]; - span[2] = _encode32Chars[(int)(value >> 50) & 31]; - span[1] = _encode32Chars[(int)(value >> 55) & 31]; - span[0] = _encode32Chars[(int)(value >> 60) & 31]; + char[] encode32Chars = s_encode32Chars; + buffer[12] = encode32Chars[value & 31]; + buffer[11] = encode32Chars[(value >> 5) & 31]; + buffer[10] = encode32Chars[(value >> 10) & 31]; + buffer[9] = encode32Chars[(value >> 15) & 31]; + buffer[8] = encode32Chars[(value >> 20) & 31]; + buffer[7] = encode32Chars[(value >> 25) & 31]; + buffer[6] = encode32Chars[(value >> 30) & 31]; + buffer[5] = encode32Chars[(value >> 35) & 31]; + buffer[4] = encode32Chars[(value >> 40) & 31]; + buffer[3] = encode32Chars[(value >> 45) & 31]; + buffer[2] = encode32Chars[(value >> 50) & 31]; + buffer[1] = encode32Chars[(value >> 55) & 31]; + buffer[0] = encode32Chars[(value >> 60) & 31]; }); } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs index 6d3e8647d7c3..a6c641d59429 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/CorrelationIdGenerator.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure internal static class CorrelationIdGenerator { // Base32 encoding - in ascii sort order for easy text based sorting - private static readonly string _encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; + private static readonly char[] s_encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV".ToCharArray(); // Seed the _lastConnectionId for this application instance with // the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001 @@ -20,25 +20,23 @@ internal static class CorrelationIdGenerator private static string GenerateId(long id) { - // The following routine is ~310% faster than calling long.ToString() on x64 - // and ~600% faster than calling long.ToString() on x86 in tight loops of 1 million+ iterations - // See: https://github.com/aspnet/Hosting/pull/385 - - return string.Create(13, id, (span, value) => + return string.Create(13, id, (buffer, value) => { - span[12] = _encode32Chars[(int)value & 31]; - span[11] = _encode32Chars[(int)(value >> 5) & 31]; - span[10] = _encode32Chars[(int)(value >> 10) & 31]; - span[9] = _encode32Chars[(int)(value >> 15) & 31]; - span[8] = _encode32Chars[(int)(value >> 20) & 31]; - span[7] = _encode32Chars[(int)(value >> 25) & 31]; - span[6] = _encode32Chars[(int)(value >> 30) & 31]; - span[5] = _encode32Chars[(int)(value >> 35) & 31]; - span[4] = _encode32Chars[(int)(value >> 40) & 31]; - span[3] = _encode32Chars[(int)(value >> 45) & 31]; - span[2] = _encode32Chars[(int)(value >> 50) & 31]; - span[1] = _encode32Chars[(int)(value >> 55) & 31]; - span[0] = _encode32Chars[(int)(value >> 60) & 31]; + char[] encode32Chars = s_encode32Chars; + + buffer[12] = encode32Chars[value & 31]; + buffer[11] = encode32Chars[(value >> 5) & 31]; + buffer[10] = encode32Chars[(value >> 10) & 31]; + buffer[9] = encode32Chars[(value >> 15) & 31]; + buffer[8] = encode32Chars[(value >> 20) & 31]; + buffer[7] = encode32Chars[(value >> 25) & 31]; + buffer[6] = encode32Chars[(value >> 30) & 31]; + buffer[5] = encode32Chars[(value >> 35) & 31]; + buffer[4] = encode32Chars[(value >> 40) & 31]; + buffer[3] = encode32Chars[(value >> 45) & 31]; + buffer[2] = encode32Chars[(value >> 50) & 31]; + buffer[1] = encode32Chars[(value >> 55) & 31]; + buffer[0] = encode32Chars[(value >> 60) & 31]; }); } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs index 93189a1f89a1..edc3a8eeed95 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs @@ -109,7 +109,7 @@ public static unsafe bool TryGetAsciiString(byte* input, char* output, int count return isValid; } - private static readonly string _encode16Chars = "0123456789ABCDEF"; + private static readonly char[] s_encode16Chars = "0123456789ABCDEF".ToCharArray(); /// /// A faster version of String.Concat(, , .ToString("X8")) @@ -126,26 +126,27 @@ public static string ConcatAsHexSuffix(string str, char separator, uint number) length += str.Length; } - return string.Create(length, (str, separator, number), (charBuffer, tuple) => + return string.Create(length, (str, separator, number), (buffer, tuple) => { + var (tupleStr, tupleSeparator, tupleNumber) = tuple; + char[] encode16Chars = s_encode16Chars; + var i = 0; - if (tuple.str != null) + if (tupleStr != null) { - for (i = 0; i < tuple.str.Length; i++) - { - charBuffer[i] = tuple.str[i]; - } + tupleStr.AsSpan().CopyTo(buffer); + i = tupleStr.Length; } - - charBuffer[i + 8] = _encode16Chars[(int)tuple.number & 0xF]; - charBuffer[i + 7] = _encode16Chars[(int)(tuple.number >> 4) & 0xF]; - charBuffer[i + 6] = _encode16Chars[(int)(tuple.number >> 8) & 0xF]; - charBuffer[i + 5] = _encode16Chars[(int)(tuple.number >> 12) & 0xF]; - charBuffer[i + 4] = _encode16Chars[(int)(tuple.number >> 16) & 0xF]; - charBuffer[i + 3] = _encode16Chars[(int)(tuple.number >> 20) & 0xF]; - charBuffer[i + 2] = _encode16Chars[(int)(tuple.number >> 24) & 0xF]; - charBuffer[i + 1] = _encode16Chars[(int)(tuple.number >> 28) & 0xF]; - charBuffer[i] = tuple.separator; + + buffer[i + 8] = encode16Chars[tupleNumber & 0xF]; + buffer[i + 7] = encode16Chars[(tupleNumber >> 4) & 0xF]; + buffer[i + 6] = encode16Chars[(tupleNumber >> 8) & 0xF]; + buffer[i + 5] = encode16Chars[(tupleNumber >> 12) & 0xF]; + buffer[i + 4] = encode16Chars[(tupleNumber >> 16) & 0xF]; + buffer[i + 3] = encode16Chars[(tupleNumber >> 20) & 0xF]; + buffer[i + 2] = encode16Chars[(tupleNumber >> 24) & 0xF]; + buffer[i + 1] = encode16Chars[(tupleNumber >> 28) & 0xF]; + buffer[i] = tupleSeparator; }); }