On the performance of Yield.SetCanceled function
ClockGet opened this issue · 5 comments
My English is not great, so I try to briefly describe my question.
I saw your source code, When I use the IAsyncEnumerableExtensions.Take function to returns a specified number of contiguous elements, I noticed that in Yield class ReturnAsync return a TaskCompletionSource object called _resumeEnumerationTcs, when AsyncEnumeratorWithState disposed, it will set yield Canceled, which triggered _resumeEnumerationTcs to set exception. I find out exception would reduce performance, Is it's deliberately designed like this? And can be replaced by TrySetCanceled? Thanks~
OK, it's not clear what's going on, so here is some info first:
- The
_resumeEnumerationTcs
is set to Canceled state by design. Please see section "4: Clean-up on incomplete enumeration" in the readme file for more info. TrySetCanceled
won't change anything. TheSetCanceled
method internally callsTrySetCanceled
, checks for the result, and throws an exception onfalse
.
And help me to understand the problem:
- What kind of performance degradation do you observe?
- How do you measure that performance?
- Do you have
using
statements inside your async enumeration lambda function?
Thank you for your reply, I saw the readme file, it solves some of my doubts, and:
- I focus on how the excuting time of the same operation is as short as possible, so I think the execution time with using TrySetException may become longer.
- My programming is not as good as my English, so I wrote some simple code like:
static async Task TestNoTry()
{
await Task.Run(() =>
{
TaskCompletionSource<int> taskCompletionSource = new TaskCompletionSource<int>();
taskCompletionSource.TrySetResult(1);
return taskCompletionSource.Task;
}).ContinueWith(task => { });
}
static async Task TestHaveException()
{
await Task.Run(() =>
{
TaskCompletionSource<int> taskCompletionSource = new TaskCompletionSource<int>();
taskCompletionSource.TrySetException(new Exception());
return taskCompletionSource.Task;
}).ContinueWith(task => { });
}
static async Task TestHaveCanceled()
{
await Task.Run(() =>
{
TaskCompletionSource<int> taskCompletionSource = new TaskCompletionSource<int>();
taskCompletionSource.TrySetCanceled();
return taskCompletionSource.Task;
}).ContinueWith(task => { });
}
static async Task Run()
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int loop = 0; loop < 100000; loop++)
{
await TestNoTry();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Restart();
for (int loop = 0; loop < 100000; loop++)
{
await TestHaveException();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Restart();
for (int loop = 0; loop < 100000; loop++)
{
await TestHaveCanceled();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
Through these codes, I though it might be more efficient to use TrySetCanceled than TrySetException.
3. No, I don't have.
Well, thanks for the code, but I think you miss the crucial understanding about how async-await and TPL work, so the code tests nothing but speed of task execution using TPL.
And how is it related to this library?
I just use the code to test speed of task execution, and test that TrySetException
would cost more time than TrySetCanceled
, and proved that in this library when ReturnAsync
return a task with a exception would cost more time, maybe I misunderstood async-await, TPL and your library, thanks again for your reply!
Yes, it's slower indeed. But if difference in ~2500 nanoseconds per iteration really matters for your application, you should not use neither IAsyncEnumerable
nor IEnumerable
:) That difference is negligible compared to heavy IO awaiting, which is the main target for this library.