2010-09-06 1 views
3

У меня есть очень большой 2D ByteArray в памяти,Можно ли сделать массив двумерных байтов одним огромным массивом непрерывных байтов?

byte MyBA = new byte[int.MaxValue][10]; 

Есть ли способ (вероятно небезопасные), что я могу обмануть C#, думая, что это один огромный непрерывный массив байтов? Я хочу сделать это, чтобы передать его MemoryStream, а затем BinaryReader.

MyReader = new BinaryReader(MemoryStream(*MyBA)) //Syntax obviously made-up here 
+0

Предполагая, что int 32 бита, по 10 каждый, не делает ваш массив 40 гигабайтами? –

+0

Я понимаю вашу проблему, но почему вы пытаетесь выделить такой большой массив? Я не сомневаюсь, что даже если вам удастся решить эту техническую проблему, ваша архитектура ошибочна, и программа, которую вы пытаетесь разработать, в конечном итоге не будет работать. – Gilad

+0

Gilad, мне нужно выделить большой массив, чтобы иметь возможность обрабатывать его в памяти. Для меня очень важна скорость. Кроме того, у меня много потоков, обрабатывающих одни и те же данные одновременно. Я понимаю вашу озабоченность, но в этой области уже работают данные с точностью до граничного предела, который является максимальным размером байтаря. Если бы я мог удалить этот предел (или дурак C#), то я не вижу причин, по которым он не должен продолжать работать. – ManInMoon

ответ

0

Вы можете создать MemoryStream, а затем передать массив в одну строку с помощью метода Write

EDIT: Предел MemoryStream, конечно, объем памяти для данного приложения. Возможно, есть предел ниже, но если вам нужно больше памяти, вам следует подумать об изменении общей архитектуры. Например. вы можете обрабатывать свои данные в кусках, или вы можете сделать механизм обмена файлом.

+0

YEs - Я делаю это. НО я считаю, что MemoryStream по-прежнему имеет максимальное ограничение на то, сколько вы можете записать на него, - то же самое, что и максимальный размер bytearray ... – ManInMoon

1

Согласен. В любом случае у вас есть ограничение размера массива.

Если вам действительно нужно управлять огромными массивами в потоке, напишите свой собственный класс потока памяти.

+1

Да _ Я это рассмотрел, но это дает мне отдельную проблему при попытке для чтения через границы, которые могут быть связаны с пользовательским классом потока, - у меня есть еще один вопрос, открытый на этом. – ManInMoon

7

Я не верю, что .NET предоставляет это, но довольно просто реализовать свою собственную реализацию System.IO.Stream, которая легко переключает вспомогательный массив. Вот (непроверенные) Основы:

public class MultiArrayMemoryStream: System.IO.Stream 
{ 
    byte[][] _arrays; 
    long _position; 
    int _arrayNumber; 
    int _posInArray; 

    public MultiArrayMemoryStream(byte[][] arrays){ 
     _arrays = arrays; 
     _position = 0; 
     _arrayNumber = 0; 
     _posInArray = 0; 
    } 

    public override int Read(byte[] buffer, int offset, int count){ 
     int read = 0; 
     while(read<count){ 
      if(_arrayNumber>=_arrays.Length){ 
       return read; 
      } 
      if(count-read <= _arrays[_arrayNumber].Length - _posInArray){ 
       Buffer.BlockCopy(_arrays[_arrayNumber], _posInArray, buffer, offset+read, count-read); 
       _posInArray+=count-read; 
          _position+=count-read; 
       read=count; 
      }else{ 
       Buffer.BlockCopy(_arrays[_arrayNumber], _posInArray, buffer, offset+read, _arrays[_arrayNumber].Length - _posInArray); 
       read+=_arrays[_arrayNumber].Length - _posInArray; 
          _position+=_arrays[_arrayNumber].Length - _posInArray; 
       _arrayNumber++; 
       _posInArray=0; 
      } 
     } 
     return count; 
    } 

    public override long Length{ 
     get { 
      long res = 0; 
      for(int i=0;i<_arrays.Length;i++){ 
       res+=_arrays[i].Length; 
      } 
      return res; 
     } 
    } 

    public override long Position{ 
     get { return _position; } 
     set { throw new NotSupportedException(); } 
    } 

    public override bool CanRead{ 
     get { return true; } 
    } 

    public override bool CanSeek{ 
     get { return false; } 
    } 

    public override bool CanWrite{ 
     get { return false; } 
    } 

    public override void Flush(){ 
    } 

    public override void Seek(long offset, SeekOrigin origin){ 
     throw new NotSupportedException(); 
    } 

    public override void SetLength(long value){ 
     throw new NotSupportedException(); 
    } 

    public override void Write(byte[] buffer, int offset, int count){ 
     throw new NotSupportedException(); 
    }  
} 

Другим способ обойти размерное ограничение 2^31 байт является UnmanagedMemoryStream который реализует System.IO.Stream поверх буфера неуправляемых памятей (который может быть столь же большим, как опоры OS). Что-то вроде этого может работать (непроверено):

var fileStream = new FileStream("data", 
    FileMode.Open, 
    FileAccess.Read, 
    FileShare.Read, 
    16 * 1024, 
    FileOptions.SequentialScan); 
long length = fileStream.Length; 
IntPtr buffer = Marshal.AllocHGlobal(new IntPtr(length)); 
var memoryStream = new UnmanagedMemoryStream((byte*) buffer.ToPointer(), length, length, FileAccess.ReadWrite); 
fileStream.CopyTo(memoryStream); 
memoryStream.Seek(0, SeekOrigin.Begin); 
// work with the UnmanagedMemoryStream 
Marshal.FreeHGlobal(buffer); 
+0

Расмус - я не слышал об этом - я посмотрю - спасибо – ManInMoon

+0

Rasmus - это выглядит интересно. – ManInMoon

+0

Не могли бы вы посоветовать мне, как лучше всего загружать байтовый поток с диска в UnmanagedMemoryStream в примере? – ManInMoon

0

Я думаю, что вы можете использовать линейную структуру вместо 2D-структуры, используя следующий подход.

Вместо того, чтобы иметь байт [int.MaxValue] [10], вы можете иметь байт [int.MaxValue * 10]. Вы должны указать элемент в [4,5] как int.MaxValue * (4-1) + (5-1). (Общая формула будет (я-1) * число столбцов + (J-1).

Конечно, вы могли бы использовать другую конвенцию.

+0

Причина, по которой он использует 2D-структуру, состоит в том, что он прошел ограничение размера одного байтового массива. – Dave

+0

хорошая точка, int.MaxValue! – DaeMoohn

0

Если я правильно понимаю ваш вопрос, вы получили массивный файл, который вы хотите прочитать в памяти и затем обработать.Но вы не можете этого сделать, потому что количество данных в файле превышает объем любого одномерного массива.

Вы упомянули, что скорость важна, и что у вас есть несколько потоков, работающих параллельно, чтобы обрабатывать данные как можно быстрее. Если вам придется разделить данные для каждого потока в любом случае, почему бы не основывать количество потоков на количестве буферов byte[int.MaxValue] покрыть все?

+0

Извините, должно было сделать это ясно. Каждый из моих потоков работает по всем данным. – ManInMoon

+0

Я вижу. Итак, вы делаете что-то вроде применения нескольких фильтров на данных и не используете потоки для обработки одного набора данных быстрее? Просто пытаюсь понять, есть ли у вас другой подход, чтобы обойти ограничение этой памяти. – Dave

0

Если вы используете Framework 4.0, у вас есть возможность работать с MemoryMappedFile. Файлы с отображением памяти могут поддерживаться физическим файлом или файлом подкачки Windows. Файлы с отображением памяти действуют как поток в памяти, прозрачно меняя данные в/из хранилища резервных копий, если и когда это необходимо.

Если вы не используете Framework 4.0, вы все равно можете использовать эту опцию, но вам нужно либо написать свой собственный, либо найти exsiting-обертку.Я ожидаю, что на The Code Project будет много.

+2

Спасибо, Крис. Я пробовал эту опцию, но MemoryMappedFiles очень медленный. – ManInMoon

+0

@ManInMoon, это несчастливо. Снижение производительности, вероятно, связано с переходом от пространства пользователя к пространству ядра, поскольку MemoryMappedFiles являются объектами ядра. –

+0

@ManInMoon, каков источник данных? Является ли это чтение из файла в память? –