Performance Tuning WCF Services, Part 2

Applies to: Windows Communication Foundation

Published: June 2011

Author: Alex Culp

Referenced Image

This topic contains the following sections.

  • Performance Implications of Binding Types
  • Optimizing Clients by Using Asynchronous Methods
  • Conclusion
  • Additional Resources

Performance Implications of Binding Types

Two of the most important decisions to make when you first design your WCF services are the binding type and configuration to use. Each binding type has its own security, interoperability and performance profile. For detailed information about WCF bindings, see "WCF Bindings in Depth," at https://msdn.microsoft.com/en-us/magazine/cc163394.aspx.

BasicHttpBinding vs. WSHttpBinding

The BasicHttpBinding and the WSHttpBinding are designed for interoperability and they are the two most commonly used bindings. Both binding types work best in a load-balanced environment. For performance, the BasicHttpBinding has considerably less overhead than the WSHttpBinding. If you do not need the features that are specific to WSHttpBinding, use the BasicHttpBinding to improve performance. To illustrate the overhead of the WSHttpBinding, compare two simple messages that invoke the GetData operation. (This operation is automatically created by using the WCF Service Application project template in Visual Studio.) One message was sent by the BasicHttpBinding. The other was sent by the WSHttpBinding. Both bindings use the default configuration. The first example is the message that was sent with the BasicHttpBinding.

GetData Operation over BasicHttpBinding Using Default Configuration

POST http://snoopy/WsVsBasic/Service1.svc HTTP/1.1
Content-Type: text/xml; charset=utf-8
VsDebuggerCausalityData: uIDPoyFpGm9Q0LxBncg8pXY/K9wAAAAA7umzxHYtQ0Kz31zWhssmrnev+tAUeJZNhyYPs2ksx4sACQAA
SOAPAction: "http://tempuri.org/IWsTest/GetData"
Host: snoopy
Content-Length: 157
Expect: 100-continue
Accept-Encoding: gzip, deflate
Connection: Keep-Alive

<s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetData xmlns="http://tempuri.org/"><value>0</value></GetData></s:Body></s:Envelope>

The next message was sent with the WSHttpBinding.

GetData Operation over WSHttpBinding Using Default Configuration

POST http://snoopy/WsVsBasic/Service1.svc HTTP/1.1
Content-Type: application/soap+xml; charset=utf-8
VsDebuggerCausalityData: 
uIDPoxkHVFNlmc5Jv2ANMRsf0v4AAAAAVTaR89FkPEu6SAUXv3BevhCzapkM5mdBu7FX238sj1cACQAA
Host: snoopy
Content-Length: 6082
Expect: 100-continue
Accept-Encoding: gzip, deflate

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" 
xmlns:a="http://www.w3.org/2005/08/addressing" 
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-
1.0.xsd"><s:Header><a:Action s:mustUnderstand="1" 
u:Id="_2">http://tempuri.org/IWsTest/GetData</a:Action><a:MessageID 
u:Id="_3">urn:uuid:3145f7f9-9627-4df1-8829-66527bbdf63a</a:MessageID><a:ReplyTo 
u:Id="_4"><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo><a:To 
s:mustUnderstand="1" u:Id="_5">http://snoopy/WsVsBasic/Service1.svc</a:To><o:Security 
s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-
wssecurity-secext-1.0.xsd"><u:Timestamp u:Id="uuid-6b8649fe-eb7c-47ee-b6b2-6ffbb11a1228-
14"><u:Created>2011-05-31T18:04:00.125Z</u:Created><u:Expires>2011-05-
31T18:09:00.125Z</u:Expires></u:Timestamp><c:SecurityContextToken u:Id="uuid-444f22b9-9410-
4c58-9ab0-cdf276c8a744-4" 
xmlns:c="https://schemas.xmlsoap.org/ws/2005/02/sc"><c:Identifier>urn:uuid:f27207b4-6169-
42f6-8c01-0281f1016cff</c:Identifier></c:SecurityContextToken><c:DerivedKeyToken u:Id="uuid-
6b8649fe-eb7c-47ee-b6b2-6ffbb11a1228-9" 
xmlns:c="https://schemas.xmlsoap.org/ws/2005/02/sc"><o:SecurityTokenReference><o:Reference 
ValueType="https://schemas.xmlsoap.org/ws/2005/02/sc/sct" URI="#uuid-444f22b9-9410-4c58-9ab0-
cdf276c8a744-
4"/></o:SecurityTokenReference><c:Offset>0</c:Offset><c:Length>24</c:Length><c:Nonce>unyKdx2wSrGlZ2zlknS59A==</c:Nonce></c:DerivedKeyToken><c:DerivedKeyToken 
u:Id="uuid-6b8649fe-eb7c-47ee-b6b2-6ffbb11a1228-10" 
xmlns:c="https://schemas.xmlsoap.org/ws/2005/02/sc"><o:SecurityTokenReference><o:Reference 
ValueType="https://schemas.xmlsoap.org/ws/2005/02/sc/sct" URI="#uuid-444f22b9-9410-4c58-9ab0-
cdf276c8a744-
4"/></o:SecurityTokenReference><c:Nonce>v/0z8buJ20+CMdJaVWuqrA==</c:Nonce></c:DerivedKeyToken><e:ReferenceList 
xmlns:e="http://www.w3.org/2001/04/xmlenc#"><e:DataReference URI="#_1"/><e:DataReference 
URI="#_6"/></e:ReferenceList><e:EncryptedData Id="_6" 
Type="http://www.w3.org/2001/04/xmlenc#Element" 
xmlns:e="http://www.w3.org/2001/04/xmlenc#"><e:EncryptionMethod 
Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/><KeyInfo 
xmlns="http://www.w3.org/2000/09/xmldsig#"><o:SecurityTokenReference><o:Reference 
ValueType="https://schemas.xmlsoap.org/ws/2005/02/sc/dk" URI="#uuid-6b8649fe-eb7c-47ee-b6b2-
6ffbb11a1228-
10"/></o:SecurityTokenReference></KeyInfo><e:CipherData><e:CipherValue>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</e:CipherValue></e:CipherData></e:EncryptedData></o:Security></s:Header><s:Body 
u:Id="_0"><e:EncryptedData Id="_1" Type="http://www.w3.org/2001/04/xmlenc#Content" 
xmlns:e="http://www.w3.org/2001/04/xmlenc#"><e:EncryptionMethod 
Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/><KeyInfo 
xmlns="http://www.w3.org/2000/09/xmldsig#"><o:SecurityTokenReference 
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-
1.0.xsd"><o:Reference ValueType="https://schemas.xmlsoap.org/ws/2005/02/sc/dk" URI="#uuid-
6b8649fe-eb7c-47ee-b6b2-6ffbb11a1228-
10"/></o:SecurityTokenReference></KeyInfo><e:CipherData><e:CipherValue>3QMtCMZQJjmofI9hhy3qF3WSNjFmJr2tP/35cVzULipPKvrvV7+kQwaB6c9skzmiBt4fNkYtkfB447tr2pSoe41FlvRlex33tEqzO8J6yBM=</e:CipherValue></e:CipherData></e:EncryptedData></s:Body></s:Envelope>

As you can see, the BasicHttpBinding message has a length of 157 bytes compared to a length of 6082 bytes for the WSHttpBinding message. That is an overhead of approximately 6KB, which is applied to every operation call on your service. Even if you turn off security entirely (in which case, you might as well use BasicHttpBinding) you still have quite a bit of overhead, as you can see from the following message.

GetData Operation over WSHttpBinding Without Security

POST http://snoopy/WsVsBasic/Service1.svc HTTP/1.1
Content-Type: application/soap+xml; charset=utf-8
VsDebuggerCausalityData: 
uIDPo8NrAvFYvppNtyW6FEkOqogAAAAAMIvzI3E7KUieYx1HKjww0iCQkuSRLEJCpsd1LROwtCoACQAA
Host: snoopy
Content-Length: 533
Expect: 100-continue
Accept-Encoding: gzip, deflate
Connection: Keep-Alive

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" 
xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><a:Action 
s:mustUnderstand="1">http://tempuri.org/IWsTest/GetData</a:Action><a:MessageID>urn:uuid:a6dad2c7-cfe1-46a8-a8c5-
b4c0b09381a6</a:MessageID><a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo><a:To 
s:mustUnderstand="1">http://snoopy/WsVsBasic/Service1.svc</a:To></s:Header><s:Body><GetData 
xmlns="http://tempuri.org/"><value>0</value></GetData></s:Body></s:Envelope>

In this example, the overall size of the message is reduced to 533 bytes. This is an improvement, but the binding also provides no more functionality than if you used the BasicHttpBinding.

Binary Encoding over HTTP

TCP is the fastest binding for two-way communications between servers. However, TCP bindings do not perform well in load-balanced environments. If only WCF clients consume your services, it is possible to get the performance benefits of a TCP binding without incurring its drawbacks by using a binary encoding over HTTP. WCF does not provide this binding, but you can create your own. The following XML code shows this custom binding.

<bindings>
  <customBinding>
    <binding name="NetHttpBinding">
      <reliableSession />
      <compositeDuplex />
      <oneWay />
      <binaryMessageEncoding />
      <httpTransport />
    </binding>
  </customBinding>
  <basicHttpBinding>
    <binding name="BasicMtom" messageEncoding="Mtom" />
  </basicHttpBinding>
  <wsHttpBinding>
    <binding name="NoSecurityBinding">
      <security mode="None" />
    </binding>
  </wsHttpBinding>
</bindings>
<services>
  <service name="WsVsBasic.Service1">
    <endpoint address="" binding="customBinding" bindingConfiguration="NetHttpBinding"
      contract="WsVsBasic.IWsTest" />
  </service>
</services>

Invoking the GetData operation, and using the custom binding yields a message whose length is only 121 bytes. This is even smaller than the message sent by using the BasicHttpBinding, which required 384 bytes for the same message. The message that was sent with the custom binding is not shown because a binary encoding yields many unprintable characters. Basically, when you use a binary encoding you do not have the overhead of the XML tags, which often account for more bytes than the data itself. For more information about encoders, see "Choosing a Message Encoder," at https://msdn.microsoft.com/en-us/library/aa751889.aspx. A binary encoder is the most efficient way to encode messages when you have a WCF client.

Net.Tcp Binding

The Net.Tcp binding is the best performing binding type for two-way communications between different servers. One drawback is that it is not interoperable with non-WCF clients. It also does not work well with load balancing. If you use the Net.Tcp binding in a load-balanced environment, you will have difficulties. This is because the TCP handshake creates an affinity between the client and the server, as opposed to the client and the load balancer, or the load balancer and the server. If you want to use the Net.tcp binding, plan to implement some retry logic. The better alternative is to use binary encoding over HTTP, which has the benefits of a high-performance serializer without the complications of using TCP in a load-balanced environment. For more information about load balancing see https://msdn.microsoft.com/en-us/library/ms730128(VS.90).aspx.

Named Pipes Binding

The named pipes binding can be used to communicate between different processes on the same machine. It is the fastest binding if you communicate between two processes on the same server.

MSMQ Binding

One of the most common mistakes that developers of WCF services make is to ignore one-way contracts and the MSMQ binding. Often, services return a response that indicates the success or failure of the individual operation. However, there are also situations where the client does not need to know whether an operation succeeded or failed, and instead simply trusts that the work will be done properly. The MSMQ binding provides a reliable way to submit messages to a service, but it does not send reply messages back to the client. The benefit of this is that clients do not have to wait for the message to be processed to resume execution.

Optimizing Clients by Using Asynchronous Methods

Often, the appearance of good performance is more important than the reality. It may not always be possible to optimize a service so that it can return a response in fractions of a second. It might make more sense to optimize the client to do other work while it waits for the results from the service. Fortunately, WCF makes it easy to optimize clients by providing asynchronous methods, so that you do not have to write your own threading logic. For more information about Synchronous and Asynchronous Operatios see https://msdn.microsoft.com/en-us/library/ms734701.aspx. To call WCF Service operations asychronously see: How to: Call WCF Service Operations Asynchronously https://msdn.microsoft.com/en-us/library/ms730059.aspx.

Conclusion

Performance tuning WCF services is more of an art than a science. This article provides explanations of some optimization techniques. When you encounter performance problems, make sure to work your way from the biggest performance issue to the smallest, until you are satisfied with the service's performance.

Additional Resources

For more information, see the following resources.

Previous article: Performance Tuning WCF Services, Part 1