DDC

Hi,

I've been doing some performance testing and want to check my conclusions for the results of one of my scenarios.

Previous scenarios exercised variations of a test where each iteration invoked a workflow which ran to completion.

SequenceActivity
BeginActivity

EndActivity

Parameters are passed in and out of the workflow and between activities via dependency properties.

In a failed attempt to improve both throughput and response time, I created an event-driven workflow that is only created and started once. Iterations are initiated by calling an iterate method on a local service. The local service queues an item for consumption by a custom activity. The basic idea was to avoid per iteration lifecycle costs.

Here's the workflow layout:

SequenceActivity
EventHandlingScopeActivity
HandleExternalEventActivity // waits for shutdown event
EventHandlersActivity
EventDrivenActivity
EventHandlingSequence : SequenceActivity, IEventActivity
IterateAndBeginActivity
EndResultActivity

The main activity contained by the eventhandling scope simply waits for a shutdown event. The real work is done by the custom activities in the event handling sequence. Input parameters are passed into the workflow via the local service iterate call. IterateAndBeginActivity consumes the queue item and deposits the result in a dependency property which is bound to an input on the EndResultActivity.

EndResultActivity's output is passed to a local service that raises an event and passes the iteration result back to the host application.

However, rather than performance improvement, I got drastic reduction. Here are some sample measurements.

Measurement Simple Workflow Event-Driven Workflow
Throughput (iterations per sec) 2253.5 430.1
No-load response time (seconds) 0.0007187 0.0022719

The main cause appears to be that the EventHandlersActivity creates a new ActivityExecutionContext for each event, and the resultant cloning of activities incurs a heavy serialization/deserialization cost.

If that's correct so far, then I have the following questions.

Are the per event AECs used for any features besides compensation logic and parallel execution

Would it be possible to write a custom EventHandlersActivity that allows per event AECs to be disabled when compensation isn't required

David



Re: Windows Workflow Foundation EventHandlingScope eventhandler performance

Tom Lake - MSFT

A good starting place for any performance question is here, it is the official performance document. Your assumption about the creation of a new AEC for each event that comes into the EventHandlersActivity is correct. It happens whenever an activity is expected to run more than once. Take a look at Nate's blog on spawned contexts here.




Re: Windows Workflow Foundation EventHandlingScope eventhandler performance

DDC

Thanks for the confirmation. I had read the performance document and Nates blog. Both helped me to my conclusion. But I still have the questions. The performance document advises:

For greater performance, activity writers should develop custom activities that are not as flexible as those in the base activity library, but satisfy specific business requirements and provide optimal performance.

Basically, I'm wondering if writing a custom EventHandlersActivity that omitted per-event AEC creation would achieve optimal performance if I needed high throughput and low response time but didn't care about compensation or parallel execution. Or would it be such a departure from the workflow norm that it wouldn't even work

David





Re: Windows Workflow Foundation EventHandlingScope eventhandler performance

Tom Lake - MSFT

If an activity executes multiple time each time the activity executes it MUST have a new execution context.  An activity can not go from Closed back to Initialized or Executing.  I am not sure of what Marc was trying to say, I'll check with him.




Re: Windows Workflow Foundation EventHandlingScope eventhandler performance

Tom Lake - MSFT

Marc meant that to increase performance you should limit the complexity of the activity tree for any activity that will be getting cloned. Each time a new execution context is created a clone of the activity, and all of its children, is made. As an example if you had the following activity tree:

While

Sequence

Code

And the code run in code activity is the following:

if (true)

{

// Do something

}

else

{

// Do something else

}

It will be more performant than the following activity tree:

While

Sequence

IfElse

IfElseBranch

Code // Do something

IfElseBranch

Code // Do something else