Непосредственно читающий большой двоичный файл в копировании C# w/out

Я ищу самый эффективный/прямой способ сделать эту простую операцию C/C++:

void ReadData(FILE *f, uint16 *buf, int startsamp, int nsamps)
{
   fseek(f, startsamp*sizeof(uint16), SEEK_SET);
   fread(buf, sizeof(uint16), nsamps, f);
}

в C#/.NET. (Я игнорирую возвращаемые значения для ясности - производственный код проверил бы их.) А именно, я должен считать во многих (потенциально 10-е к 100's миллионов) 2-байтовые (16-разрядные) "ushort" образцы целочисленных данных (фиксированный формат, никакой требуемый парсинг) сохраненный в двоичном файле в дисковом файле. Хорошая вещь о пути C состоит в том, что он читает образцы непосредственно в "uint16 *" буфер без участия ЦП и никакое копирование. Да, это потенциально "небезопасно", поскольку это использует пусто * указатели на буферы неизвестного размера, но кажется, что должна быть "безопасная" альтернатива.NET.

Что лучший способ состоит в том, чтобы выполнить это в C#? Я озирался и столкнулся с несколькими подсказками (использование "объединений" FieldOffset, "небезопасный" код с помощью указателей, Упорядочивая), но ни один, кажется, вполне не работает на эту ситуацию, w/out использование своего рода копирования/преобразования. Я хотел бы избежать BinaryReader. ReadUInt16 (), так как это очень медленно и интенсивный ЦП. На моей машине существует о 25x различие в скорости между для () цикл с ReadUInt16 () и чтение байтов непосредственно в байт [] массив с единственным Read (). То отношение могло быть еще выше с неблокированием ввода-вывода (перекрывающий "полезную" обработку при ожидании диска ввода-вывода).

Идеально, я хотел бы просто "замаскировать" ushort [] массив как байт [] массив, таким образом, я мог заполнить его непосредственно Read () или так или иначе иметь Read (), заполняют ushort [] массив непосредственно:

// DOES NOT WORK!!
public void GetData(FileStream f, ushort [] buf, int startsamp, int nsamps)
{
    f.Position = startsamp*sizeof(ushort);
    f.Read(buf, 0, nsamps);
}

Но нет никакого Read () метода, который берет ushort [] массив, только байт [] массив.

Это может быть сделано непосредственно в C#, или я должен использовать неуправляемый код или стороннюю библиотеку, или я должен обратиться к интенсивному ЦП преобразованию образца образцом? Хотя "безопасный" предпочтен, хорошо с использованием "небезопасного" кода или некоторого приема с Маршалом, я просто еще не понял это.

Спасибо за любое руководство!


[ОБНОВЛЕНИЕ]

Я хотел добавить некоторый код, как предложено dtb, как там, чтобы казаться, быть очень небольшим количеством примеров ReadArray вокруг. Это - очень простое, w/no показанная проверка ошибок.

public void ReadMap(string fname, short [] data, int startsamp, int nsamps)
{
    var mmf = MemoryMappedFile.CreateFromFile(fname);
    var mmacc = mmf.CreateViewAccessor();

    mmacc.ReadArray(startsamp*sizeof(short), data, 0, nsamps);
}

Данные безопасно выводятся в Ваш переданный массив. Можно также указать тип для более составных типов. Это кажется способным для выведения простых типов самостоятельно, но со спецификатором типа, это было бы похоже на это:

    mmacc.ReadArray<short>(startsamp*sizeof(short), data, 0, nsamps);

[UPATE2]

Я хотел добавить код, как предложено ответом победы Ben, в форме "скелета", подобной вышеупомянутому, для сравнения. Этот код был скомпилирован и протестирован, и работы, и является FAST. Я использовал тип SafeFileHandle непосредственно в DllImport (вместо более обычного IntPtr) для упрощения вещей.

[DllImport("kernel32.dll", SetLastError=true)]
[return:MarshalAs(UnmanagedType.Bool)]
static extern bool ReadFile(SafeFileHandle handle, IntPtr buffer, uint numBytesToRead, out uint numBytesRead, IntPtr overlapped);

[DllImport("kernel32.dll", SetLastError=true)]
[return:MarshalAs(UnmanagedType.Bool)]
static extern bool SetFilePointerEx(SafeFileHandle hFile, long liDistanceToMove, out long lpNewFilePointer, uint dwMoveMethod);

unsafe void ReadPINV(FileStream f, short[] buffer, int startsamp, int nsamps)
{
    long unused; uint BytesRead;
    SafeFileHandle nativeHandle = f.SafeFileHandle; // clears Position property
    SetFilePointerEx(nativeHandle, startsamp*sizeof(short), out unused, 0);

    fixed(short* pFirst = &buffer[0])
        ReadFile(nativeHandle, (IntPtr)pFirst, (uint)nsamps*sizeof(short), out BytesRead, IntPtr.Zero);
}
6
задан dale 10 July 2010 в 19:28
поделиться

2 ответа

ответ dtb - это еще лучший способ (на самом деле, он также должен копировать данные, никакого выигрыша там нет), но я просто хотел указать, что для извлечения ushort значений из байтового массива вы должны использовать BitConverter а не BinaryReader

EDIT: пример кода для p/invoking ReadFile:

[DllImport("kernel32.dll", SetLastError=true)]
[return:MarshalAs(UnmanagedType.Bool)]
static extern bool ReadFile(IntPtr handle, IntPtr buffer, uint numBytesToRead, out uint numBytesRead, IntPtr overlapped);

[DllImport("kernel32.dll", SetLastError=true)]
[return:MarshalAs(UnmanagedType.Bool)]
static extern bool SetFilePointerEx(IntPtr hFile, long liDistanceToMove, out long lpNewFilePointer, uint dwMoveMethod);

unsafe bool read(FileStream fs, ushort[] buffer, int offset, int count)
{
  if (null == fs) throw new ArgumentNullException();
  if (null == buffer) throw new ArgumentNullException();
  if (offset < 0 || count < 0 || offset + count > buffer.Length) throw new ArgumentException();
  uint bytesToRead = 2 * count;
  if (bytesToRead < count) throw new ArgumentException(); // detect integer overflow
  long offset = fs.Position;
  SafeFileHandle nativeHandle = fs.SafeFileHandle; // clears Position property
  try {
    long unused;
    if (!SetFilePositionEx(nativeHandle, offset, out unused, 0);
    fixed (ushort* pFirst = &buffer[offset])
      if (!ReadFile(nativeHandle, new IntPtr(pFirst), bytesToRead, out bytesToRead, IntPtr.Zero)
        return false;
    if (bytesToRead < 2 * count)
      return false;
    offset += bytesToRead;
    return true;
  }
  finally {
    fs.Position = offset; // restore Position property
  }
}
2
ответ дан 9 December 2019 в 22:28
поделиться

Вы можете использовать MemoryMappedFile . После отображения файла в память вы можете создать представление (то есть MemoryMappedViewAccessor ), которое предоставляет метод ReadArray . Этот метод может читать структуры из файла без маршалинга и работает с примитивными типами lie ushort .

8
ответ дан 9 December 2019 в 22:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: