Dimitri C.
Dimitri C.

Reputation: 22526

What can be done to speed up synchronous WCF calls?

My performance measurements of synchronous WCF calls from within a Silverlight application showed I can make 7 calls/s on a localhost connection, which is very slow. Can this be speeded up, or is this normal?

This is my test code:

const UInt32 nrCalls = 100;
ICalculator calculator = new CalculatorClient(); // took over from the MSDN calculator example
for (double i = 0; i < nrCalls; ++i)
{
    var call = calculator.BeginSubtract(i + 1, 1, null, null);
    call.AsyncWaitHandle.WaitOne();
    double result = calculator.EndSubtract(call);
}

Remarks:

Update: I've ported the client side from Silverlight to .NET, and this solved the performance problem. In that test, synchronous calls are made at 140 calls/s (instead of 7 calls/s), and asynchronous calls at 200 calls/s (instead of 16 calls/s). Apparently, the slowness is inherent to the Silverlight platform. I'll have to learn to live with it.

Upvotes: 4

Views: 1339

Answers (2)

Brian
Brian

Reputation: 118915

Use async calls and run them in parallel.

The internet is only so fast. As it is, you send a request, wait for an eternity for (1) the message to reach the server, (2) the server to respond, (3) the response to travel back. (1) and (3) take time; there are things like 'miles to the server' and 'the speed of light' in play, maybe. And then you send the next request and do the same waiting game again. And again. In a loop.

Hence async calls and parallel requests = win.

(Hint: if you use F#, async can be very easy, as in the sample below.)

open System
open System.Diagnostics 
open System.ServiceModel 

let binding = new BasicHttpBinding()
let address = "http://YOURSERVERMACHINENAME:11111/Blah"

#if SERVER
[<ServiceContract>]
type IMyContract =
    [<OperationContract>]
    abstract member Subtract : x:int * y:int -> int

type MyService() =
    interface IMyContract with
        member this.Subtract(x,y) = x-y

let host = new ServiceHost(typeof<MyService>, new Uri(address))
host.AddServiceEndpoint(typeof<IMyContract>, binding, address) |> ignore
let smb = new Description.ServiceMetadataBehavior()
smb.HttpGetEnabled <- true
host.Description.Behaviors.Add(smb)
host.Open()
Console.WriteLine("service is open")

#else

[<ServiceContract(Name="IMyContract")>]
type IMyClientContract =
    [<OperationContract>]
    abstract member Subtract : x:int * y:int -> int
    [<OperationContract(AsyncPattern=true)>]
    abstract member BeginSubtract : x:int * y:int * c:AsyncCallback * o:obj -> IAsyncResult
    abstract member EndSubtract : r:IAsyncResult -> int

let client = ChannelFactory<IMyClientContract>.CreateChannel(binding, new EndpointAddress(address))
let MAX = 30

let syncSw = Stopwatch.StartNew()
[1..MAX] |> Seq.iter (fun i -> 
    let r = client.Subtract(i,1)
    Console.WriteLine(r))
Console.WriteLine("sync took {0}ms", syncSw.ElapsedMilliseconds)

let AsyncSubtract(x,y) = Async.FromBeginEnd(x, y, client.BeginSubtract, client.EndSubtract)

let asyncSw = Stopwatch.StartNew()
[1..MAX] |> Seq.map (fun i -> async {
    let! r = AsyncSubtract(i,1)
    Console.WriteLine(r)}) 
    |> Async.Parallel |> Async.RunSynchronously |> ignore
Console.WriteLine("async took {0}ms", asyncSw.ElapsedMilliseconds)
#endif

Console.WriteLine("press a key to quit")
Console.ReadKey()

Upvotes: 1

TomTom
TomTom

Reputation: 62157

Not a lot. You run the problem of making 100 http calls, that just takes time. You arelady do not blow it by generating a new client every time.... so, sorry.

In general, this is a bad example (or: a good one showing bad practice). Services should always be coarse grained to avoid the call overhead. A service should have some "weight" in what it does, generally.

A calculator may accept an array of operations, so all 100 calculations can be sent at once, for example.

Keep that in mind when when designing your own interfaces.

Upvotes: 3

Related Questions