WCF - AsyncPattern Performance

2019-07-23 20:48发布

问题:

I wrote the following WCF Service which implement two methods. They both do the same thing but one of them is synchronous, the other one asynchronous. The result of the load test (using Pylot) are disappointing. I get an almost identical throughput from the sync and async version of the method (see performance numbers below).

I deployed the service in a real IIS site (not Cassini) and I am seeing the same pattern. What am I doing wrong? I was under the impression that using the WCF AsyncPattern I would be able to increase the load. Is my assumption correct? Am I doing something wrong?

Thanks!

[ServiceContract]
public class Service1
{
    // Synchronous version
    [WebGet(UriTemplate = "/sync")]
    public List<SampleItem> GetSamples()
    {
        return this.LongRunningMethod();
    }

    // Asynchronous version - Begin
    [WebGet(UriTemplate = "/async")]
   [OperationContract(AsyncPattern = true)]
    public IAsyncResult BeginGetSampleAsync(AsyncCallback callback, object state)
    {
        var task = Task<List<SampleItem>>.Factory.StartNew(() => this.LongRunningMethod());

        Func<List<SampleItem>> getSampleItems = () => this.LongRunningMethod();

        return getSampleItems.BeginInvoke(callback, state);
    }

    // Asynchronous version - End
    public List<SampleItem> EndGetSampleAsync(IAsyncResult result)
    {
        AsyncResult typedAsyncResult = (AsyncResult)result;
        Func<List<SampleItem>> func = (Func<List<SampleItem>>)typedAsyncResult.AsyncDelegate;

        return func.EndInvoke(result);
    }

    private List<SampleItem> LongRunningMethod()
    {
        // Simulate some load... I/O operations
        Thread.Sleep(500);

        return new List<SampleItem>() { new SampleItem() { Id = 1, StringValue = "Hello" } };
    } 

Below are the perf. numbers.

Performance Numbers
-------------------------------------------------
Test parameters:
  number of agents:          500
  test duration in seconds:  60
  rampup in seconds:         0
  interval in milliseconds:  0
  test case xml:             **syncTestCase.xml**
  log messages:              False


Started agent 500

All agents running...


[################100%##################]  60s/60s

Requests:  3836
Errors: 0
Avg Response Time:  7.286
Avg Throughput:  63.92
Current Throughput:  70
Bytes Received:  852036

-------------------------------------------------   

Test parameters:
  number of agents:          500
  test duration in seconds:  60
  rampup in seconds:         0
  interval in milliseconds:  0
  test case xml:             **asyncTestCase.xml**
  log messages:              False


Started agent 500

All agents running...


[################100%##################]  60s/60s

Requests:  3884
Errors: 0
Avg Response Time:  7.176
Avg Throughput:  64.66
Current Throughput:  620
Bytes Received:  862248

-------------------------------------------------

回答1:

Your test is very artificial. Asynchronous programming is best for heavily I/O-bound operations which should not require waiting on a running thread (rather, they should be based on IO-completion ports or similar). Your test uses thread pool threads (BeginInvoke/EndInvoke) and blocks them (Thread.Sleep) which almost guarantees that you will end up doing worse than in the sync case where you don't eat up any extra threads.

In short: don't use server-side async unless you have I/O-bound work to do on the server side -- if you are CPU bound, stick to a synchronous server implementation (potentially in combination with a MaxConcurrentCalls setting equal to the number of CPU cores on the server).