c# .net memory-mapped-files

c# - ¿Cómo puedo leer rápidamente los bytes de un archivo asignado en memoria en.NET?



memory-mapped-files (4)

En algunas situaciones, la clase MemoryMappedViewAccessor simplemente no la corta para leer bytes de manera eficiente; Lo mejor que obtenemos es el ReadArray<byte> genérico, que es la ruta para todas las estructuras e implica varios pasos innecesarios cuando solo necesita bytes.

Es posible usar un MemoryMappedViewStream , pero como se basa en un Stream , primero debe buscar la posición correcta, y luego la operación de lectura en sí misma tiene muchos más pasos innecesarios.

¿Existe una forma rápida y de alto rendimiento para leer una matriz de bytes de un archivo mapeado en memoria en .NET, dado que debería ser un área particular del espacio de direcciones para leer?


Esta solución requiere código no seguro (compilar con el /unsafe ), pero toma un puntero a la memoria directamente; entonces Marshal.Copy puede ser utilizado. Esto es mucho, mucho más rápido que los métodos proporcionados por el marco .NET.

// assumes part of a class where _view is a MemoryMappedViewAccessor object public unsafe byte[] ReadBytes(int offset, int num) { byte[] arr = new byte[num]; byte *ptr = (byte*)0; this._view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr); Marshal.Copy(IntPtr.Add(new IntPtr(ptr), offset), arr, 0, num); this._view.SafeMemoryMappedViewHandle.ReleasePointer(); return arr; } public unsafe void WriteBytes(int offset, byte[] data) { byte* ptr = (byte*)0; this._view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr); Marshal.Copy(data, 0, IntPtr.Add(new IntPtr(ptr), offset), data.Length); this._view.SafeMemoryMappedViewHandle.ReleasePointer(); }


Sé que esta es una pregunta más antigua que ha sido respondida pero quería agregar mis dos centavos.

Realicé una prueba con la respuesta aceptada (utilizando el código no seguro) y con el enfoque MemoryMappedViewStream para leer una matriz de bytes de 200 MB.

MemoryMappedViewStream

const int MMF_MAX_SIZE = 209_715_200; var buffer = new byte[ MMF_VIEW_SIZE ]; using( var mmf = MemoryMappedFile.OpenExisting( "mmf1" ) ) using( var view = mmf.CreateViewStream( 0, buffer.Length, MemoryMappedFileAccess.ReadWrite ) ) { if( view.CanRead ) { Console.WriteLine( "Begin read" ); sw.Start( ); view.Read( buffer, 0, MMF_MAX_SIZE ); sw.Stop( ); Console.WriteLine( $"Read done - {sw.ElapsedMilliseconds}ms" ); } }

Realicé la prueba 3 veces con cada enfoque y recibí los siguientes tiempos.

MemoryMappedViewStream:

  1. 483ms
  2. 501ms
  3. 490ms

Método inseguro

  1. 531ms
  2. 517ms
  3. 523ms

Desde la pequeña cantidad de pruebas, parece que el MemoryMappedViewStream tiene una ventaja muy leve . Con eso en mente para cualquiera que lea este post en el MemoryMappedViewStream me gustaría ir con MemoryMappedViewStream .


Una versión segura de esta solución es:

var file = MemoryMappedFile.CreateFromFile(...); var accessor = file.CreateViewAccessor(); var bytes = new byte[yourLength]; // assuming the string is at the start of the file // aka position: 0 // https://msdn.microsoft.com/en-us/library/dd267761(v=vs.110).aspx accessor.ReadArray<byte>( position: 0, // The number of bytes in the accessor at which to begin reading array: bytes, // The array to contain the structures read from the accessor offset: 0, // The index in `array` in which to place the first copied structure count: yourLength // The number of structures of type T to read from the accessor. ); var myString = Encoding.UTF8.GetString(bytes);

He probado esto, funciona. No puedo comentar sobre su rendimiento o si es la MEJOR solución global, simplemente funciona.


Ver este informe de error: no hay forma de determinar el desplazamiento interno utilizado por MemoryMappedViewAccessor: hace que la propiedad SafeMemoryMappedViewHandle sea inutilizable.

Del informe:

MemoryMappedViewAccessor tiene una propiedad SafeMemoryMappedViewHandle, que devuelve el ViewHandle utilizado internamente por el MemoryMappedView, pero no tiene ninguna propiedad para devolver el desplazamiento utilizado por el MemoryMappedView.

Como MemoryMappedView es una página que alinea el desplazamiento solicitado en MemoryMappedFile.CreateViewAccessor (desplazamiento, tamaño), es imposible utilizar SafeMemoryMappedViewHandle para cualquier cosa útil sin conocer el desplazamiento.

Tenga en cuenta que lo que realmente queremos hacer es utilizar el método AcquirePointer (ref byte * pointer) para permitir que se ejecute un código de puntero rápido (posiblemente no administrado). Estamos de acuerdo con que el puntero esté alineado con la página, pero debe ser posible averiguar cuál es el desplazamiento de la dirección solicitada originalmente.