6

Suppose you have a method that wraps an inner long-running method. This outer method may do a tiny amount of work before/after calling said long-running method. For example:

public async Task<int> LongRunningWrapperAsync() { int result = await LongRunningAsync(); result++; return result; } 

It seems like the added weight of the boilerplate code generated by using async is not necessarily worth the benefit of using await, since its continuation is basically trivial. Therefore, given a sufficiently trivial* continuation, is it more performant to use Task.ContinueWith? E.g.

public Task<int> LongRunningWrapperAsync() { return LongRunningAsync().ContinueWith(task => task.Result + 1, TaskContinuationOptions.ExecuteSynchronously); } 

* Yes, both 'sufficiently' and 'trivial' are vague terms. Also, I've ignored exception handling in this contrived example. I suppose the need to handle exceptions implies that the continuation is non-trivial.

2
  • 1
    Just try it yourself - use System.Diagnostics.Stopwatch and execute each code a large number of times. Commented Jan 28, 2014 at 10:35
  • 2
    I disagree with the "trivial"ness of your example. async/await will wire up Exceptional cases as well as your happy case to your continuation. This allows for much easier development. The point of using libraries and frameworks is often the edge cases around a trivial concept. Commented Jan 28, 2014 at 10:46

1 Answer 1

12

Therefore, given a sufficiently trivial* continuation, is it more performant to use Task.ContinueWith?

Yes, but I argue that this is the wrong question to ask.

It is more performant. However, you have to be very careful to handle edge cases (in particular, any exceptions raised by LongRunningAsync would get wrapped in an AggregateException by your code). In addition, await will capture a context by default, and resume the method in that context. You can handle special cases in a more performant way via ContinueWith, but you can't handle the general case in a more performant way.

But performance is the wrong question to ask anyway. I argue that a better question to ask is: Is the code sufficiently performant, and if so, which solution is more maintainable?

Consider how many times the code will be executed. Millions? How much time will be saved? A few nanoseconds? How much developer time with the ContinueWith approach cost? Every time anyone looks at the code, it takes that much longer to see what it's doing. It is a far, far better decision to save developer time by making the code more maintainable (concentrating the savings in your company) than it is to save an absolutely minuscule amount of time when the code is run (spreading the savings across all your clients - and spreading it so thin that no single client will even be aware of it).

Sign up to request clarification or add additional context in comments.

1 Comment

Yes, agree this is a good point. Normally it is better to code for maintainability than performance as developer time costs more than run time, as you say. However, I think the question is still a valid one, because when some code isn't sufficiently performant, it's good to know what options are available to try to improve performance (plus benchmarks to prove it). Knowing that async/await generates a state machine and replacing them with ContinueWith may do away with those is good knowledge, though wisdom is knowing when to use that knowledge.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.