Как я могу быстро прочитать байты из файла сопоставленной памяти в .NET? - PullRequest
20 голосов
/ 31 октября 2011

В некоторых ситуациях класс MemoryMappedViewAccessor просто не позволяет эффективно читать байты;лучшее, что мы получаем - это общий ReadArray<byte>, который используется для всех структур и включает несколько ненужных шагов, когда вам просто нужны байты.

Можно использовать MemoryMappedViewStream, но поскольку он основан на Stream сначала нужно найти правильную позицию, а затем в самой операции чтения есть еще много ненужных шагов.

Существует ли быстрый и высокопроизводительный способ чтения массива байтов из отображенной памятифайл в .NET, учитывая, что это должна быть просто определенная область адресного пространства для чтения?

Ответы [ 4 ]

31 голосов
/ 31 октября 2011

Для этого решения требуется небезопасный код (скомпилировать с переключателем /unsafe), но он получает указатель на память напрямую;тогда Marshal.Copy можно использовать.Это намного, намного быстрее, чем методы, предоставляемые .NET Framework.

    // 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();
    }
2 голосов
/ 25 февраля 2012

См. Этот отчет об ошибке: Нет способа определить внутреннее смещение, используемое MemoryMappedViewAccessor - делает свойство SafeMemoryMappedViewHandle неприменимым.

Из отчета:

MemoryMappedViewAccessor имеетсвойство SafeMemoryMappedViewHandle, которое возвращает ViewHandle, используемый внутренне MemoryMappedView, но не имеет никакого свойства для возврата смещения, используемого MemoryMappedView.

Поскольку MemoryMappedView является страницей, выравнивающей смещение, запрошенное в MemoryMappedFile.CreateViewAccessor(смещение, размер) невозможно использовать SafeMemoryMappedViewHandle для чего-либо полезного, не зная смещения.

Обратите внимание, что на самом деле мы хотим использовать метод AcquirePointer (ref byte * pointer), чтобы разрешить некоторый быстрый указательоснованный (возможно, неуправляемый) код для запуска.Мы в порядке, когда указатель выровнен по странице, но должна быть возможность определить смещение от первоначально запрошенного адреса.

1 голос
/ 03 ноября 2017

Я знаю, что это старый вопрос, на который дан ответ, но я хотел добавить свои два цента.

Я выполнил тест как с принятым ответом (с использованием небезопасного кода), так и с подходом MemoryMappedViewStream длячтение байтового массива 200 МБ.

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" );
            }
        }

Я выполнил тест 3 раза с каждым подходом и получил следующее время.

MemoryMappedViewStream:

  1. 483мс
  2. 501мс
  3. 490мс

Небезопасный метод

  1. 531мс
  2. 517 мс
  3. 523 мс

Из небольшого количества испытаний похоже, что MemoryMappedViewStream имеет очень небольшое преимущество.Имея это в виду для тех, кто читает этот пост в будущем, я бы пошел с MemoryMappedViewStream.

1 голос
/ 24 мая 2017

Безопасная версия этого решения:

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);

Я проверил это, оно работает. Я не могу комментировать его производительность или если это ЛУЧШЕЕ общее решение, просто оно работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...