I am trying to learn about asynchronous programming and how I can benefit from it.
My hope is that I can use it to improve performance whenever I'm looping over a method that takes an significant time to complete, like the following method.
string AddStrings() { string result = ""; for (int i = 0; i < 10000; i++) { result += "hi"; } return result; } Obviously this method doesn't have much value, and I purposely made it ineffecient, in order to test the benefits of asynchronous programming. The test is done by looping over the method 100 times, first synchronously and then asynchronously.
Stopwatch watch = new Stopwatch(); watch.Start(); List<string> results = WorkSync(); //List<string> results = await WorkAsyncParallel(); watch.Stop(); Console.WriteLine(watch.ElapsedMilliseconds); List<string> WorkSync() { var stringList = new List<string>(); for (int i = 0; i < 100; i++) { stringList.Add(AddStrings()); } return stringList; } async Task<List<string>> WorkAsyncParallel() { var taskList = new List<Task<string>>(); for (int i = 0; i < 100; i++) { taskList.Add(Task.Run(() => AddStrings())); } var results = (await Task.WhenAll(taskList)).ToList(); return results; } Super optimistically (naively), I was hoping that the asynchronous loop would be 100 times as fast as the synchronous loop, as all the tasks are running at the same time. While that didn't exactly turn out to be the case, the time of the loop was decreased by more than two thirds, from around 5000 miliseconds to 1500 miliseconds!
Now my questions are:
- What makes the asynchronous loop faster than the synchronous loop, but not nearly 100 times faster? I'm guessing each of the 100 tasks are fighthing for a limited amount of CPU?
- Is this a valid method to improve performance when looping methods?
Thank you in advance.
AddStringsmethod could be improved as you are using string concatenation, you could modify it to use aStringBuilderasync/await. For that things likeParallel.ForEach, parallel LINQ (Enumerable.AsParallel) or the TPL Dataflow library are more suitable, which are easier thanTask.Run(the first two, in any case) and potentially have less overhead (as noTasks need to be allocated, even though this is a light-weight operation). A 100-fold speedup of purely synchronous work is possible only if your machine actually has 100 processors.