recently i've been working on an application that is supposed to be a thin layer on top of several services, this application is mainly doing mappings from different data format to a unified format (a lot of mappings). we use what i call "linq async" but I would like to see whether I can improve its performance.
I did a simple experiment like the following
using System;
using System.Collections.Async;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
int max = 100;
int[] input = Enumerable.Range(1, max).ToArray();
Work w = new Work();
// senario 1
sw.Restart();
var result1 = input.Select(x => w.LongHoursWork(x)).ToArray();
sw.Stop();
Console.WriteLine($"One Thread: {sw.ElapsedMilliseconds}");
// senario 2
sw.Restart();
var result2 = MainAsync(input, w).GetAwaiter().GetResult();
sw.Stop();
Console.WriteLine($"Linq async: {sw.ElapsedMilliseconds}");
// senario 3
sw.Restart();
var result3 = input.AsParallel().Select(x => w.LongHoursWork(x)).ToArray();
sw.Stop();
Console.WriteLine($"Parallel: {sw.ElapsedMilliseconds}");
// check result
for (int i = 0; i < max; i++)
{
if(result1[i] == result2[i] && result2[i] == result3[i])// && result4.Contains(result1[i]))
continue;
throw new ArgumentNullException();
}
Console.ReadKey();
}
static async Task<string[]> MainAsync(int[] input, Work work)
{
var result = await Task.WhenAll(input.Select(async x => await work.LongHoursWorkAsync(x)));
return result;
}
}
public class Work
{
public string LongHoursWork(int i)
{
for (int ix = 0; ix < 100000; ix++)
{
i += ix;
}
return i.ToString();
}
public async Task<string> LongHoursWorkAsync(int i)
{
return await Task.FromResult(LongHoursWork(i));
}
}
}
//execution result:
//One Thread: 88
//Linq async: 97
//Parallel: 59
// if i change the input array to 600(similar to what i have in production
// environment)
// execution result:
// One Thread: 347
// Linq async: 292
// Parallel: 101
I expect the senario2(linq async) should be more or less the same as senario3(parallel) as senario2 will create a lot of tasks and execute them concurrently. However, my simple experiment tells me im wrong. Senario2 is more or less the same as senario1. this confuses me a lot and I tried reading many related questions in stackoverflow but cannot find a satisfied answer. Most of the questions are talking about non-blocking UI, whereas my application is a backend service. Also I read about linq async will improve the throughput for a backend service under load, but from my experiment im not convinced. If it's not superior in performance for a single run, how can it improve the throughput under load??? Also i noticed the more item in the input array, the better performance i can get from parallel executing(scenario3). So I would like to ask my question here.
- is linq async not create a lot of tasks and execute them concurrently? why are they almost same as single thread?? Then what's the point of using them??
- for one call, parallel seems the fastest, how about under heavy load? which one is fastest and why??
- shall i rewrite linq async with parallel?? performance is important in my case
- or rather is there any flaws in my experiment?? should i do a I/O bound experiment?
Many thanks in advance.