Why is WCF performance so slow when compared with Remoting?
.net, remoting, wcf
Solution
This tests sequential, synchronous, calls only. An IIS hosted WCF service provides more infrastructure to handle a higher load and will likely out-perform remoting for a high-load test (one with a lot of concurrent connections).
A remoting endpoint can also be hosted in IIS though to get the same advantages. WCF can also be hosted in a console. You're really comparing apples to oranges here.
Problem
I'm trying to evaluate which communication technology to use in a new system, right now it looks like Remoting is our only option as WCF's performance is terrible. I have benchmarked calling a WCF service hosted in IIS7 using nettcp, compared to calling a remoting interface hosted in a console app. The WCF service takes ~4.5 seconds to perform 1000 requests, synchronusoly on an endpoint (which simply returns back a new instantce of an object). The remoting client takes < 0.5 second to perform the same task. Here is the WCF client code: ``` public class TcpNewsService : INewsService { private INewsService _service = null; Lazy<ChannelFactory<INewsService>> _newsFactory = new Lazy<ChannelFactory<INewsService>>(() => { var tcpBinding = new NetTcpBinding { //MaxBufferPoolSize = int.MaxValue, //MaxBufferSize = int.MaxValue, //MaxConnections = int.MaxValue, //MaxReceivedMessageSize = int.MaxValue, PortSharingEnabled=false, TransactionFlow = false, ListenBacklog = int.MaxValue, Security = new NetTcpSecurity { Mode = SecurityMode.None, Transport = new TcpTransportSecurity { ProtectionLevel = System.Net.Security.ProtectionLevel.None, ClientCredentialType = TcpClientCredentialType.None }, Message = new MessageSecurityOverTcp { ClientCredentialType = MessageCredentialType.None } }, ReliableSession = new OptionalReliableSession { Enabled = false } }; EndpointAddress endpointAddress = new EndpointAddress("net.tcp://localhost:8089/NewsService.svc"); return new ChannelFactory<INewsService>(tcpBinding, endpointAddress); }); public TcpNewsService() { _service = _newsFactory.Value.CreateChannel(); ((ICommunicationObject)_service).Open(); } public List<NewsItem> GetNews() { return _service.GetNews(); } } ``` And a simple console app to invoke the client code: ``` var client = new TcpNewsService(); Console.WriteLine("Getting all news"); var sw = new System.Diagnostics.Stopwatch(); sw.Start(); for (int i = 0; i < 1000; i++) { var news = client.GetNews(); } sw.Stop(); Console.WriteLine("Finished in " + sw.Elapsed.TotalSeconds); Console.ReadLine(); ``` The web.config file for the IIS host looks like this: ``` <system.serviceModel> <services> <service behaviorConfiguration="NewsServiceBehavior" name="RiaSpike.News.Service.NewsService"> <endpoint address="" binding="netTcpBinding" bindingConfiguration="tcpBinding" contract="RiaSpike.News.Types.INewsService"> </endpoint> <endpoint address="http://localhost:8094/NewsService.svc" binding="basicHttpBinding" bindingConfiguration="httpBinding" contract="RiaSpike.News.Types.INewsService"> </endpoint> </service> </services> <behaviors> <serviceBehaviors> <behavior name="NewsServiceBehavior"> <serviceDebug includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors> <bindings> <basicHttpBinding> <binding name="httpBinding"> <security mode="None"> <transport clientCredentialType="None" /> </security> </binding> </basicHttpBinding> <netTcpBinding> <binding name="tcpBinding" portSharingEnabled="false"> <security mode="None"> <transport clientCredentialType="None" /> <message clientCredentialType="None" /> </security> <reliableSession enabled="false" /> </binding> </netTcpBinding> </bindings> ``` And the service class hosted in IIS: ``` [ServiceBehavior( ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single, AddressFilterMode = AddressFilterMode.Any )] public class NewsService : MarshalByRefObject, INewsService { public List<NewsItem> GetNews() { return new List<NewsItem> { new NewsItem { Descripion = "The Description", Id = 1, Title = "The Title"} }; } } ``` I have traced the WCF activity and have seen the process take approximately 5 millisconds to complete (I couldn't upload animage, here's one acivity trace from the log) From: Processing message 5. Transfer 3/12/2010 15:35:58.861 Activity boundary. Start 3/12/2010 15:35:58.861 Received a message over a channel. Information 3/12/2010 15:35:58.861 To: Execute 'Ria.Spike.News.INewsService.GetNews' Transfer 3/12/2010 15:35:58.864 Activity boundary. Suspend 3/12/2010 15:35:58.864 From: Execute 'Ria.Spike.News.INewsService.GetNews' Transfer 3/12/2010 15:35:58.864 Activity boundary. Resume 3/12/2010 15:35:58.864 Sent a message over a channel Information 3/12/2010 15:35:58.866 Activity boundary. Stop 3/12/2010 15:35:58.866 Is this as good as it gets :s Here's the remoting code used in this example. ``` var iserver = (INewsService)Activator.GetObject(typeof(INewsService), "tcp://127.0.0.1:9000/news"); var sw = new System.Diagnostics.Stopwatch(); sw.Start(); for (int i = 0; i < 1000; i++) { var news = iserver.GetNews(); } sw.Stop(); Console.WriteLine("Finished in " + sw.Elapsed.TotalSeconds); Console.ReadLine(); ``` And a the TCP endpoint for this remoting channel hosting in IIS: ``` public class Global : System.Web.HttpApplication { private TcpServerChannel _quote; protected void Application_Start(object sender, EventArgs e) { _quote = new TcpServerChannel(9000); if (ChannelServices.RegisteredChannels.Length ==0) { ChannelServices.RegisterChannel(_quote, false); } RemotingConfiguration.RegisterWellKnownServiceType( typeof(NewsService), "news", WellKnownObjectMode.SingleCall); _quote.StartListening(null); } } ```