Any 'quick wins' to make .NET remoting faster on a single machine?
Asked Answered
L

5

6

I've been badly let-down and received an application that in certain situations is at least 100 times too slow, which I have to release to our customers very soon (a matter of weeks).

Through some very simple profiling I have discovered that the bottleneck is its use of .NET Remoting to transfer data between a Windows service and the graphical front-end - both running on the same machine.

Microsoft guidelines say "Minimize round trips and avoid chatty interfaces": write

MyComponent.SaveCustomer("bob", "smith");

rather than

MyComponent.Firstname = "bob";
MyComponent.LastName = "smith";
MyComponent.SaveCustomer();

I think this is the root of the problem in our application. Unfortunately calls to MyComponent.* (the profiler shows that 99.999% of the time is spent in such statements) are scattered liberally throughout the source code and I don't see any hope of redesigning the interface in accordance with the guidelines above.

Edit: In fact, most of the time the front-end reads properties from MyComponent rather than writes to it. But I suspect that MyComponent can change at any time in the back-end.

I looked to see if I can read all properties from MyComponent in one go and then cache them locally (ignoring the change-at-any-time issue above), but that would involve altering hundreds of lines of code.

My question is: Are they any 'quick-win' things I can try to improve performance?

I need at least a 100-times speed-up. I am a C/C++/Delphi programmer and am pretty-much unfamiliar with C#/.NET/Remoting other than what I have read up on in the last couple of days. I'm looking for things that can be completed in a few days - a major restructuring of the code is not an option.

Just for starters, I have already confirmed that it is using BinaryFormatter.

(Sorry, this is probably a terrible question along the lines of 'How can I feasibly fix X if I rule out all of the feasible options'… but I'm desperate!)

Edit 2 In response to Richard's comment below: I think my question boils down to:

  1. Is there any setting I can change to reduce the cost of a .NET Remoting round-trip when both ends of the connection are on the same machine?
  2. Is there any setting I can change to reduce the number of round-trips - so that each invocation of a remote object property doesn't result in a separate round-trip? And might this break anything?
Linebreeding answered 12/11, 2010 at 10:19 Comment(3)
Your options are likely to be limited, .NET Remoting is strongly deprecated (except for inter-AppDomain) due to its lack of any security.Slumgullion
"Quick win" invariable means "slow lose" imho, be very wary of that phrase.Godiva
If this was WCF, the "null transport" might have helped, but that is a very different model to remoting.Herbart
P
7

Under .Net Remoting you have 3 ways of communicating by HTTP, TCP and IPC. If the commnuicatin is on the same pc I sugest using IPC channels it will speed up your calls.

Parsley answered 12/11, 2010 at 10:46 Comment(2)
Thank you! This is exactly the kind of answer I was hoping for. It turns out it was using HTTP. An operation that took 3 minutes 20 seconds with HTTP took 1 minute 24 with TCP, and 59 seconds with IPC. 59 seconds is still rather too long (from the user's perspective it is just expanding a node on a tree-view so it ought to be instant) but it is a great improvement. For the benefit of anyone else trying this, I had to specify authorizedGroup="Users" in the <channel> element on the server. Otherwise it was straightforward.Linebreeding
I'm making this my 'accepted answer' because it is a genuine quick-win. I still need to speed the code further though.Linebreeding
H
5

In short, no there are no quick wins here. Personally I would not make MyComponent (as a DTO) a MarshalByRefObject (which is presumably the problem), as those round trips are going to cripple you. I would keep it as a regular class, and just move a few key methods to pump them around (i.e. have a MarshalByRef manager/repository/etc class).

That should reduce round-trips; if you still have problems then it will probably be bandwidth related; this is easier to fix; for example by changing the serializer. protobuf-net allows you to do this easily by simply implementing ISerializable and forwarding the two methods (one from the interface, plus the ctor) to ProtoBuf.Serializer - it then does all the work for you, and works with remoting. I can provide examples of this if you like.

Actually, protobuf-net may help with CPU usage too, as it is a much more CPU-efficient serializer.

Herbart answered 12/11, 2010 at 11:8 Comment(0)
P
3

Could you make MyComponent a class that will cache the values and only submit them when SaveCustomer() is called?

Pondicherry answered 12/11, 2010 at 10:24 Comment(3)
I should have been clearer in my original question: most of the time the front-end seems to be reading from the remote object, not writing to it. But I suspect that the remote object can change at any time, so caching in the front-end may not be an option.Linebreeding
@IanGoldby: If you cannot reduce the network round trips, or the cost of each round trip then you are stuck. You will have to compromise (or use magic).Slumgullion
If the object is requested a lot you could still consider caching it, even if it's only for a few seconds. It really depends on how often the client requests the data and how critical it is. Good luck!Pondicherry
R
1

You can try compressing traffic. If not 100-times increase, you'll still gain some performance benefit

Ruse answered 12/11, 2010 at 10:21 Comment(2)
While I can see that being a benefit when data is transferred across a network, in my case won't it just add to the processing burden? The CPU is already pegged near to 100%.Linebreeding
@Ian - in this case it seems that round-trips are the most likely issue; compressing the traffic helps bandwidth, but it doesn't help latency.Herbart
S
1

If you need the latest data (always see the real value), and the cost of getting the data each time dominates the runtime then you need to be radical.

How about changing polling to push. Rather than calling the remote side each time you need a value, have the remote push all changes and cache the latest values locally.

Local lookups (after the initial get) are always up to date with all remoting overhead being done in the background (on another thread). Just be careful about thread safety for non-atomic types.

Slumgullion answered 12/11, 2010 at 10:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.