Using WCF from WPF very slow on first use
Asked Answered
C

3

11

I have been struggling for a few days with an issue with our WPF applications and I wonder if someone has come across this before and can help? The problem seems to boil down to the client generating "on-the-fly" a serializer to handle the types in that web method call. When that method is called for the first time (the web service itself has been running already), it may take e.g. 8 seconds, subsequent calls may take e.g. 20ms. The CPU on the client WPF process is v. high during this delay.

When using the XmlSerializer, there is a way of pre-generating these serializer assemblies, using svcutil. When (as we are) using the normal WCF DataContractSerializer, this option does not seem to be present.

What I would like is to be able to pre-generate this assembly for all types in all my data contracts (a lot) or, alternatively, to replace this process with a custom one that I can code and passes the data in binary (we own both ends of this webservice/client and they are both .NET 4). I have already used BinaryForamtter and GZip compression and while this speeds up the transfer of data, it always gets restored to XML to be de-serialized by the framework, hence this problem remains.

Any ideas?

Crider answered 11/10, 2012 at 9:3 Comment(0)
G
8

You can use a binary library such as protobuf-net, which is quite fast, even if there is an initial startup cost because code has to be generated for each type, it's still way better than DataContractSerializer or BinaryFormatter. You should gain a few seconds and have an overall smoother experience. It can be easily integrated with WCF. Keep in mind that WCF will still inspect your various contracts to generate the correct WSDL and various metadata.

There are other things that can slow down WCF startup, such as determining the default web proxy. Be sure that useDefaultWebProxy is false in your binding configuration if you don't have any use for it.

Still, you will find that WCF startup is generally slow no matter what you do to optimize it. Personally, tired of fighting slowness in a similar scenario (I controlled both ends, and the client was a WPF application), I simply ditched WCF and went for ServiceStack + protobuf-net. The first call went from 2-3 seconds to ~100ms, and all subsequent HTTP calls are really instantaneous. The overall user experience has greatly improved. Note that I'm in no way affiliated with ServiceStack, this is just my experience.

Gironde answered 11/10, 2012 at 9:27 Comment(5)
Thanks Julien, I have got useDefaultWebProxy false already. I have looked to use protobuf-net, but I am having trouble understanding wether it will require me to re-decorate all my data types with a new attribute - I hope not. It says on the web-site that v2 can be "used without attributes if you wish", but all examples use attributes. Will this improve the startup speed of the first call, even in IIS (which is really mandated), i.e. is the protobuf-net serializer pre-generated at build time?Crider
@SimonEvans, as long as you have the Order property set on every DataMember, you should be fine. Didn't use it myself though (I went with protobuf from the start).Gironde
Julien, do you mean I would have to add [ProtoMember(n)] to every public property? Thanks for your helpCrider
@SimonEvans: you can precompile the protobuf-net serializers to an assembly that you can reference if you want really fast startup. As for ProtoMember(n), it shouldn't be necessary: DataMember(Order = n) has the same effect. As for "will it really improve the startup speed", I can't guarantee you anything, try it out in a sample project.Gironde
Thanks Julien, at the moment I have no attributes set at all on my types (except KnownType and XmlInclude where appropriate). Datacontractserializer will, by default, assume all types and all members are to be serialized (which is what we want). I will have to try to decorate a few types and see if I can use protobuf-net, the trouble is we have hundreds (some of which are generated).Crider
I
2

Did you confirm by looking at the generated service reference that DataContractSerializer is indeed being used? It is possible that due to some schema mismatch during the Add Service Reference operation, XmlSerializer code was generated instead of the default DataContractSerializer which is causing this behavior typical of XmlSerializer. In this case, as you noted - you can pre-generate the serialization code to improve the cold-startup : http://msdn.microsoft.com/en-us/library/aa751883.aspx. Thanks.

Isidoro answered 12/10, 2012 at 18:6 Comment(0)
F
0

You could improve the cold-start time by preloading your WCF service...i.e. don't wait for the 1st request to cause it to be loaded...load in advance.

Just a few ideas...which "might" help speed up things in the serialization area.

Regarding the pre-generation of the serialization assemblies for your service types....there is the option in Project|Build called "Generate Serialization Assembly"....if switched "On" then it generates the assemblies at Build time rather than dynamically at run time.

It's not clear if this option is only for the pre-generation of serialization assemblies for XMLSerializer based serializers, or for DataContractSerializers too. You could try switching it to "On" to see if it made a difference.

You could also try to do this early on in the code in your client and server, to exercise the DataContract serializers...i.e. before the client or server had to deal with a request...(not sure if it would help or not).

DataContractSerializer ps = new DataContractSerializer(typeof(Person));
DataContractSerializer cs = new DataContractSerializer(typeof(Company));
etc...

To make it more maintainable you could write a routine that uses reflection to locate the types that you intend to serialize e.g. look for types marked with DataContract....or some other heuritistics....or a predefined table.

Fourgon answered 11/10, 2012 at 10:0 Comment(2)
Thanks Colin, I have tried all the above and none of this makes any difference. The problem with the last suggestion is that the issue I am trying to deal with is slow startup of the client, so it will not really matter if I do the startup for the serializers or if the framework does so, it will take the same time. Most cold start fixes relate to the server-side, which is not a problem. The "Generate Serialization Assembly" option only works for XmlSerializer and then only if VS finds a proxy (we have our own).Crider
This link might give you some ideas of different strategies to take.... icodeteam.net/default/post/iCodeTeam/35/…Fourgon

© 2022 - 2024 — McMap. All rights reserved.