C # 4.5 arquivo ler synchronization de desempenho vs asynchronous

Estamos tentando medir o desempenho entre a leitura de uma série de arquivos usando os methods sync vs async. Estava esperando ter aproximadamente o mesmo tempo entre os dois, mas o uso de async é cerca de 5.5x mais lento.

Isso pode ser devido à sobrecarga de gerenciar os segmentos, mas só queria saber sua opinião. Talvez nós estamos apenas medindo os tempos errados.

Estes são os methods que estão sendo testados:

static void ReadAllFile(string filename) { var content = File.ReadAllBytes(filename); } static async Task ReadAllFileAsync(string filename) { using (var file = File.OpenRead(filename)) { using (var ms = new MemoryStream()) { byte[] buff = new byte[file.Length]; await file.ReadAsync(buff, 0, (int)file.Length); } } } 

E este é o método que os executa e inicia o cronômetro:

  static void Test(string name, Func gettask, int count) { Stopwatch sw = new Stopwatch(); Task[] tasks = new Task[count]; sw.Start(); for (int i = 0; i < count; i++) { string filename = "file" + i + ".bin"; tasks[i] = gettask(filename); } Task.WaitAll(tasks); sw.Stop(); Console.WriteLine(name + " {0} ms", sw.ElapsedMilliseconds); } 

Que é tudo executado daqui:

  static void Main(string[] args) { int count = 10000; for (int i = 0; i  Task.Run(() => ReadAllFile(filename)), count); Test("Read Contents Async", (filename) => ReadAllFileAsync(filename), count); Console.ReadKey(); } 

E o método de gravação auxiliar:

  static void Write(string filename) { Data obj = new Data() { Header = "random string size here" }; int size = 1024 * 20; // 1024 * 256; obj.Body = new byte[size]; for (var i = 0; i < size; i++) { obj.Body[i] = (byte)(i % 256); } Stopwatch sw = new Stopwatch(); sw.Start(); MemoryStream ms = new MemoryStream(); Serializer.Serialize(ms, obj); ms.Position = 0; using (var file = File.Create(filename)) { ms.CopyToAsync(file).Wait(); } sw.Stop(); //Console.WriteLine("Writing file {0}", sw.ElapsedMilliseconds); } 

Os resultados:

 -Read Contents 574 ms -Read Contents Async 3160 ms 

Vai realmente apreciar se alguém puder lançar alguma luz sobre isso enquanto procuramos na pilha e na teia, mas não conseguimos encontrar uma explicação adequada.

Há muitas coisas erradas com o código de teste. Mais notavelmente, seu teste “asynchronous” não usa E / S assíncrona; com streams de arquivos, você precisa explicitamente abri-los como asynchronouss ou então você está apenas fazendo operações síncronas em um thread de segundo plano. Além disso, os tamanhos dos seus arquivos são muito pequenos e podem ser facilmente armazenados em cache.

Eu modifiquei o código de teste para gravar arquivos muito maiores, para ter um código de synchronization comparativo vs asynchronous, e para tornar o código asynchronous asynchronous:

 static void Main(string[] args) { Write("0.bin"); Write("1.bin"); Write("2.bin"); ReadAllFile("2.bin"); // warmup var sw = new Stopwatch(); sw.Start(); ReadAllFile("0.bin"); ReadAllFile("1.bin"); ReadAllFile("2.bin"); sw.Stop(); Console.WriteLine("Sync: " + sw.Elapsed); ReadAllFileAsync("2.bin").Wait(); // warmup sw.Restart(); ReadAllFileAsync("0.bin").Wait(); ReadAllFileAsync("1.bin").Wait(); ReadAllFileAsync("2.bin").Wait(); sw.Stop(); Console.WriteLine("Async: " + sw.Elapsed); Console.ReadKey(); } static void ReadAllFile(string filename) { using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false)) { byte[] buff = new byte[file.Length]; file.Read(buff, 0, (int)file.Length); } } static async Task ReadAllFileAsync(string filename) { using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true)) { byte[] buff = new byte[file.Length]; await file.ReadAsync(buff, 0, (int)file.Length); } } static void Write(string filename) { int size = 1024 * 1024 * 256; var data = new byte[size]; var random = new Random(); random.NextBytes(data); File.WriteAllBytes(filename, data); } 

Na minha máquina, esse teste (construído em Release, executado fora do depurador) produz esses números:

 Sync: 00:00:00.4461936 Async: 00:00:00.4429566 

Todas as operações de E / S são assíncronas. O encadeamento aguarda (fica suspenso) para que a operação de E / S seja concluída. É por isso que quando lemos jeffrey richter ele sempre diz para fazer o i / o async, para que seu thread não seja desperdiçado esperando por aí. de Jeffery Ricter

Também criar um segmento não é barato. Cada thread recebe 1 mb de espaço de endereço reservado para o modo de usuário e outro de 12kb para o modo kernel. Depois disso, o sistema operacional tem que notificar todos os dll no sistema que um novo segmento foi gerado. O mesmo acontece quando você destruir um segmento. Pense também nas complexidades da mudança de contexto

Encontrei um grande SO resposta aqui