From 028fb4588ddf22f045f7251767a7737514a5faa0 Mon Sep 17 00:00:00 2001 From: itn3000 Date: Tue, 3 Apr 2018 20:09:58 +0900 Subject: [PATCH] caching ParameterInfo of method and remove StreamReader,avoid Concat and ToArray --- .gitignore | 1 + src/SoapCore.Benchmark.Shared/Program.cs | 2 +- src/SoapCore/OperationDescription.cs | 32 ++++++++++++++++++ src/SoapCore/SoapEndpointMiddleware.cs | 43 +++++++++++------------- 4 files changed, 54 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index f1e3d20e..16dd581a 100644 --- a/.gitignore +++ b/.gitignore @@ -250,3 +250,4 @@ paket-files/ # JetBrains Rider .idea/ *.sln.iml +**/BenchmarkDotNet.Artifacts/ diff --git a/src/SoapCore.Benchmark.Shared/Program.cs b/src/SoapCore.Benchmark.Shared/Program.cs index b89eb4f2..2d68a8cc 100644 --- a/src/SoapCore.Benchmark.Shared/Program.cs +++ b/src/SoapCore.Benchmark.Shared/Program.cs @@ -21,7 +21,7 @@ namespace SoapCore.Benchmark public class EchoBench { // 0 measures overhead of creating host - [Params(0, 100)] + [Params(100)] public int LoopNum; static string EchoContent = @" diff --git a/src/SoapCore/OperationDescription.cs b/src/SoapCore/OperationDescription.cs index 2a5a2ca8..3c58ea3a 100644 --- a/src/SoapCore/OperationDescription.cs +++ b/src/SoapCore/OperationDescription.cs @@ -1,8 +1,23 @@ using System.Reflection; using System.ServiceModel; +using System.Xml; +using System.Xml.Serialization; +using System.Linq; namespace SoapCore { + public class SoapMethodParameterInfo + { + public ParameterInfo Parameter { get; private set; } + public string Name { get; private set; } + public string Namespace { get; private set; } + public SoapMethodParameterInfo(ParameterInfo parameter, string name, string ns) + { + Parameter = parameter; + Name = name; + Namespace = ns; + } + } public class OperationDescription { public ContractDescription Contract { get; private set; } @@ -11,6 +26,9 @@ public class OperationDescription public string Name { get; private set; } public MethodInfo DispatchMethod { get; private set; } public bool IsOneWay { get; private set; } + public SoapMethodParameterInfo[] NormalParameters { get; private set; } + public SoapMethodParameterInfo[] OutParameters { get;private set;} + public string ReturnName {get;private set;} public OperationDescription(ContractDescription contract, MethodInfo operationMethod, OperationContractAttribute contractAttribute) { @@ -20,6 +38,20 @@ public OperationDescription(ContractDescription contract, MethodInfo operationMe IsOneWay = contractAttribute.IsOneWay; ReplyAction = contractAttribute.ReplyAction; DispatchMethod = operationMethod; + NormalParameters = operationMethod.GetParameters().Where(x => !x.IsOut && !x.ParameterType.IsByRef) + .Select(info => CreateParameterInfo(info, contract)).ToArray(); + OutParameters = operationMethod.GetParameters().Where(x => x.IsOut || x.ParameterType.IsByRef) + .Select(info => CreateParameterInfo(info, contract)).ToArray(); + ReturnName = operationMethod.ReturnParameter.GetCustomAttribute()?.Name ?? Name + "Result"; + } + static SoapMethodParameterInfo CreateParameterInfo(ParameterInfo info, ContractDescription contract) + { + var elementAttribute = info.GetCustomAttribute(); + var parameterName = !string.IsNullOrEmpty(elementAttribute?.ElementName) + ? elementAttribute.ElementName + : info.GetCustomAttribute()?.Name ?? info.Name; + var parameterNs = elementAttribute?.Namespace ?? contract.Namespace; + return new SoapMethodParameterInfo(info, parameterName, parameterNs); } } } diff --git a/src/SoapCore/SoapEndpointMiddleware.cs b/src/SoapCore/SoapEndpointMiddleware.cs index b3d5ad7f..151efeed 100644 --- a/src/SoapCore/SoapEndpointMiddleware.cs +++ b/src/SoapCore/SoapEndpointMiddleware.cs @@ -151,12 +151,10 @@ private async Task ProcessOperation(HttpContext httpContext, IServicePr Message responseMessage; //Reload the body to ensure we have the full message - using (var reader = new StreamReader(httpContext.Request.Body)) - { - var body = await reader.ReadToEndAsync(); - var requestData = Encoding.UTF8.GetBytes(body); - httpContext.Request.Body = new MemoryStream(requestData); - } + var mstm = new MemoryStream((int)httpContext.Request.ContentLength.GetValueOrDefault(1024)); + await httpContext.Request.Body.CopyToAsync(mstm).ConfigureAwait(false); + mstm.Seek(0, SeekOrigin.Begin); + httpContext.Request.Body = mstm; //Return metadata if no request if (httpContext.Request.Body.Length == 0) @@ -195,7 +193,8 @@ private async Task ProcessOperation(HttpContext httpContext, IServicePr // Get operation arguments from message Dictionary outArgs = new Dictionary(); var arguments = GetRequestArguments(requestMessage, reader, operation, ref outArgs); - var allArgs = arguments.Concat(outArgs.Values).ToArray(); + // avoid Concat() and ToArray() cost when no out args(this may be heavy operation) + var allArgs = outArgs.Count != 0 ? arguments.Concat(outArgs.Values).ToArray() : arguments; // Invoke Operation method var responseObject = operation.DispatchMethod.Invoke(serviceInstance, allArgs); @@ -220,7 +219,7 @@ private async Task ProcessOperation(HttpContext httpContext, IServicePr } // Create response message - var resultName = operation.DispatchMethod.ReturnParameter.GetCustomAttribute()?.Name ?? operation.Name + "Result"; + var resultName = operation.ReturnName; var bodyWriter = new ServiceBodyWriter(_serializer, operation.Contract.Namespace, operation.Name + "Response", resultName, responseObject, resultOutDictionary); responseMessage = Message.CreateMessage(_messageEncoder.MessageVersion, null, bodyWriter); responseMessage = new CustomMessage(responseMessage); @@ -243,21 +242,20 @@ private async Task ProcessOperation(HttpContext httpContext, IServicePr return responseMessage; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private object[] GetRequestArguments(Message requestMessage, System.Xml.XmlDictionaryReader xmlReader, OperationDescription operation, ref Dictionary outArgs) { - var parameters = operation.DispatchMethod.GetParameters().Where(x => !x.IsOut && !x.ParameterType.IsByRef).ToArray(); - var arguments = new List(); + var parameters = operation.NormalParameters; + // avoid reallocation + var arguments = new List(parameters.Length + operation.OutParameters.Length); // Find the element for the operation's data xmlReader.ReadStartElement(operation.Name, operation.Contract.Namespace); for (int i = 0; i < parameters.Length; i++) { - var elementAttribute = parameters[i].GetCustomAttribute(); - var parameterName = !string.IsNullOrEmpty(elementAttribute?.ElementName) - ? elementAttribute.ElementName - : parameters[i].GetCustomAttribute()?.Name ?? parameters[i].Name; - var parameterNs = elementAttribute?.Namespace ?? operation.Contract.Namespace; + var parameterName = parameters[i].Name; + var parameterNs = parameters[i].Namespace; if (xmlReader.IsStartElement(parameterName, parameterNs)) { @@ -265,9 +263,9 @@ private object[] GetRequestArguments(Message requestMessage, System.Xml.XmlDicti if (xmlReader.IsStartElement(parameterName, parameterNs)) { - var elementType = parameters[i].ParameterType.GetElementType(); - if (elementType == null || parameters[i].ParameterType.IsArray) - elementType = parameters[i].ParameterType; + var elementType = parameters[i].Parameter.ParameterType.GetElementType(); + if (elementType == null || parameters[i].Parameter.ParameterType.IsArray) + elementType = parameters[i].Parameter.ParameterType; switch (_serializer) { @@ -294,16 +292,15 @@ private object[] GetRequestArguments(Message requestMessage, System.Xml.XmlDicti } } - var outParams = operation.DispatchMethod.GetParameters().Where(x => x.IsOut || x.ParameterType.IsByRef).ToArray(); - foreach (var parameterInfo in outParams) + foreach (var parameterInfo in operation.OutParameters) { - if (parameterInfo.ParameterType.Name == "Guid&") + if (parameterInfo.Parameter.ParameterType.Name == "Guid&") outArgs[parameterInfo.Name] = Guid.Empty; - else if (parameterInfo.ParameterType.Name == "String&" || parameterInfo.ParameterType.GetElementType().IsArray) + else if (parameterInfo.Parameter.ParameterType.Name == "String&" || parameterInfo.Parameter.ParameterType.GetElementType().IsArray) outArgs[parameterInfo.Name] = null; else { - var type = parameterInfo.ParameterType.GetElementType(); + var type = parameterInfo.Parameter.ParameterType.GetElementType(); outArgs[parameterInfo.Name] = Activator.CreateInstance(type); } }