C#4.5 archivo de lectura de sincronización de rendimiento vs asincrónico
file asynchronous (2)
Hay muchas cosas mal con el código de prueba. Lo más notable es que su prueba "asincrónica" no usa la E / S asincrónica; con las secuencias de archivos, tiene que abrirlas explícitamente como asíncronas o de lo contrario solo está haciendo operaciones sincrónicas en una cadena de fondo. Además, los tamaños de tus archivos son muy pequeños y se pueden almacenar fácilmente en caché.
Modifiqué el código de prueba para escribir archivos mucho más grandes, para tener un código sincronizado vs asíncrono comparable, y para hacer que el código asíncrono sea asincrónico:
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);
}
En mi máquina, esta prueba (construida en Release, ejecutándose fuera del depurador) produce estos números:
Sync: 00:00:00.4461936
Async: 00:00:00.4429566
Estamos tratando de medir el rendimiento entre leer una serie de archivos usando métodos de sincronización vs asincrónicos. Esperaba tener aproximadamente el mismo tiempo entre los dos, pero resulta que usar asincronismo es aproximadamente 5.5 veces más lento.
Esto podría deberse a la sobrecarga de administrar los hilos, pero solo quería saber su opinión. Tal vez solo estamos midiendo los tiempos mal.
Estos son los métodos que se prueban:
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);
}
}
}
Y este es el método que los ejecuta e inicia el cronómetro:
static void Test(string name, Func<string, Task> 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 se ejecuta todo desde aquí:
static void Main(string[] args)
{
int count = 10000;
for (int i = 0; i < count; i++)
{
Write("file" + i + ".bin");
}
Console.WriteLine("Testing read...!");
Test("Read Contents", (filename) => Task.Run(() => ReadAllFile(filename)), count);
Test("Read Contents Async", (filename) => ReadAllFileAsync(filename), count);
Console.ReadKey();
}
Y el método de escritura 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);
}
Los resultados:
-Read Contents 574 ms
-Read Contents Async 3160 ms
Realmente apreciaré si alguien puede arrojar algo de luz sobre esto ya que buscamos en la pila y la web, pero realmente no podemos encontrar una explicación adecuada.
Todas las operaciones de E / S son asincrónicas. El subproceso solo espera (se suspende) para que finalice la operación de E / S. Por eso, cuando lee jeffrey richter, él siempre dice que haga yo / as asincrón, para que su hilo no se desperdicie esperando. de Jeffery Ricter
Además, crear un hilo no es barato. Cada hilo obtiene 1 mb de espacio de direcciones reservado para el modo de usuario y otro de 12kb para el modo kernel. Después de esto, el sistema operativo tiene que notificar a todos los dll del sistema que se ha generado un nuevo hilo. Lo mismo sucede cuando destruyes un hilo. También piense en las complejidades del cambio de contexto
Encontré una gran respuesta SO here