Я ищу наиболее эффективный / прямой способ сделать эту простую операцию 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 миллионов) 2-байтовые (16-битные) "короткие" целочисленные образцы данных (фиксированный формат), анализ не требуется) хранится в двоичном виде в файле на диске.Хорошая вещь о способе C состоит в том, что он считывает образцы непосредственно в буфер "uint16 *" без участия ЦП и без копирования.Да, это потенциально «небезопасно», так как использует буферы void * для буферов неизвестного размера, но, похоже, должна быть «безопасная» альтернатива .NET.
Каков наилучший способ сделать этов C #?Я осмотрелся и наткнулся на несколько советов («союзы», использующие FieldOffset, «небезопасный» код с использованием указателей, Marshalling), но ни один из них не вполне подходит для этой ситуации, без какого-либо копирования / преобразования.Я хотел бы избежать BinaryReader.ReadUInt16 (), так как это очень медленно и интенсивно использует процессор.На моей машине разница между циклом for () с ReadUInt16 () и чтением байтов напрямую в массив byte [] с помощью одного Read () составляет примерно 25-кратную разницу.Это соотношение может быть даже выше при неблокирующем вводе / выводе (перекрывая «полезную» обработку при ожидании дискового ввода / вывода).
В идеале, я бы хотел просто «замаскировать» массив ushort []как массив byte [], чтобы я мог заполнить его непосредственно с помощью 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 [], только массив byte [].
Может ли это быть сделано непосредственно в C #, или мне нужно использовать неуправляемый код, или стороннюю библиотеку, или я должен прибегнуть к CPU-интенсивное преобразование по образцу?Хотя «сейф» предпочтительнее, я в порядке с «небезопасным» кодом или каким-то трюком с Маршалом, я просто еще не понял его.
Спасибо за любые указания!
[ОБНОВЛЕНИЕ]
Я хотел добавить некоторый код в соответствии с предложением dtb, так как кажется, что вокруг него есть несколько примеров ReadArray.Это очень просто, без проверки ошибок.
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]
Я хотел добавить кодв соответствии с победным ответом Бена, в виде «голых костей», аналогичном приведенному выше, для сравнения.Этот код был скомпилирован и протестирован, работает и БЫСТРО.Я использовал тип 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);
}