Pregunta

Estoy a punto de comenzar toneladas de lectura de archivos binarios, cada uno con 1000 o más registros. Los nuevos archivos se añaden constantemente, por lo que estoy escribiendo un servicio de Windows para supervisar los directorios y procesar archivos nuevos a medida que se reciben. Los archivos fueron creados con un programa en C ++. He recreado las definiciones struct en C # y puedo leer la multa de datos, pero me preocupa que la forma en que lo estoy haciendo finalmente matará a mi solicitud.

using (BinaryReader br = new BinaryReader(File.Open("myfile.bin", FileMode.Open)))
{
    long pos = 0L;
    long length = br.BaseStream.Length;

    CPP_STRUCT_DEF record;
    byte[] buffer = new byte[Marshal.SizeOf(typeof(CPP_STRUCT_DEF))];
    GCHandle pin;

    while (pos < length)
    {
        buffer = br.ReadBytes(buffer.Length);
        pin = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        record = (CPP_STRUCT_DEF)Marshal.PtrToStructure(pin.AddrOfPinnedObject(), typeof(CPP_STRUCT_DEF));
        pin.Free();

        pos += buffer.Length;

        /* Do stuff with my record */
    }
}

No creo que tenga que utilizar GCHandle porque no estoy realmente la comunicación con la aplicación de C ++, todo se está haciendo desde el código administrado, pero no sé de un método alternativo.

¿Fue útil?

Solución

Para su aplicación en particular, sólo una cosa le dará la respuesta definitiva: Perfil ella.

Dicho esto aquí son las lecciones que he aprendido al trabajar con grandes soluciones PInvoke. La manera más eficaz para reunir datos para ordenar los campos que son blittable. Es decir, el CLR puede hacer sencillo lo que equivale a un establecimiento de memoria para transferir datos entre el código nativo y administrado. En términos simples, obtener todas las matrices no en línea y cuerdas de sus estructuras. Si están presentes en la estructura nativa, ellos representan con un IntPtr y reunir los valores de la demanda en código administrado.

No he perfilado siempre la diferencia entre usar Marshal.PtrToStructure frente a tener una API nativa eliminar la referencia al valor. Esto es probablemente algo que usted debe invertir en PtrToStructure debe ser revelado como un cuello de botella a través de perfiles.

Para grandes Mariscal jerarquías en la demanda frente a tirar toda una estructura en código administrado en una sola vez. Me he encontrado con este problema más cuando se trata de grandes estructuras de árbol. Marshalling un nodo individual es muy rápido si es blittable y rendimiento inteligente que funciona con sólo el Mariscal lo que necesita en ese momento.

Otros consejos

El uso de Marshal.PtrToStructure es bastante lento. He encontrado el siguiente artículo en el que se comparaba CodeProject (y la evaluación comparativa) diferentes maneras de leer datos binarios de gran ayuda:

  

rápida lectura de ficheros binarios con C #

Además de la amplia respuesta de JaredPar, no es necesario utilizar GCHandle, puede utilizar código no seguro en su lugar.

fixed(byte *pBuffer = buffer) {
     record = *((CPP_STRUCT_DEF *)pBuffer);
}  

Todo el propósito de la declaración GCHandle / fixed es al pin / fijar el segmento de memoria particular, haciendo que la memoria inamovible desde el punto de vista de GC. Si la memoria era móvil, cualquier reubicación hacen sus punteros válido.

No está seguro de qué manera es más rápido sin embargo.

Esto puede estar fuera de los límites de su pregunta, pero yo estaría inclinado a escribir un poco de montaje en C ++ administrada que hizo un fread () o algo similarmente rápido de leer en las estructuras. Una vez que tenga que lean, puede utilizar C # para hacer todo lo que necesita con ellos.

he aquí una pequeña clase que hice hace un tiempo jugando con los archivos estructurados. que era el método más rápido que pude averiguar en el momento de ir tímida inseguro (que era lo que estaba tratando de reemplazar y mantener un rendimiento comparable.)

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;

namespace PersonalUse.IO {

    public sealed class RecordReader<T> : IDisposable, IEnumerable<T> where T : new() {

        const int DEFAULT_STREAM_BUFFER_SIZE = 2 << 16; // default stream buffer (64k)
        const int DEFAULT_RECORD_BUFFER_SIZE = 100; // default record buffer (100 records)

        readonly long _fileSize; // size of the underlying file
        readonly int _recordSize; // size of the record structure
        byte[] _buffer; // the buffer itself, [record buffer size] * _recordSize
        FileStream _fs;

        T[] _structBuffer;
        GCHandle _h; // handle/pinned pointer to _structBuffer 

        int _recordsInBuffer; // how many records are in the buffer
        int _bufferIndex; // the index of the current record in the buffer
        long _recordPosition; // position of the record in the file

        /// <overloads>Initializes a new instance of the <see cref="RecordReader{T}"/> class.</overloads>
        /// <summary>
        /// Initializes a new instance of the <see cref="RecordReader{T}"/> class.
        /// </summary>
        /// <param name="filename">filename to be read</param>
        public RecordReader(string filename) : this(filename, DEFAULT_STREAM_BUFFER_SIZE, DEFAULT_RECORD_BUFFER_SIZE) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="RecordReader{T}"/> class.
        /// </summary>
        /// <param name="filename">filename to be read</param>
        /// <param name="streamBufferSize">buffer size for the underlying <see cref="FileStream"/>, in bytes.</param>
        public RecordReader(string filename, int streamBufferSize) : this(filename, streamBufferSize, DEFAULT_RECORD_BUFFER_SIZE) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="RecordReader{T}"/> class.
        /// </summary>
        /// <param name="filename">filename to be read</param>
        /// <param name="streamBufferSize">buffer size for the underlying <see cref="FileStream"/>, in bytes.</param>
        /// <param name="recordBufferSize">size of record buffer, in records.</param>
        public RecordReader(string filename, int streamBufferSize, int recordBufferSize) {
            _fileSize = new FileInfo(filename).Length;
            _recordSize = Marshal.SizeOf(typeof(T));
            _buffer = new byte[recordBufferSize * _recordSize];
            _fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.None, streamBufferSize, FileOptions.SequentialScan);

            _structBuffer = new T[recordBufferSize];
            _h = GCHandle.Alloc(_structBuffer, GCHandleType.Pinned);

            FillBuffer();
        }

        // fill the buffer, reset position
        void FillBuffer() {
            int bytes = _fs.Read(_buffer, 0, _buffer.Length);
            Marshal.Copy(_buffer, 0, _h.AddrOfPinnedObject(), _buffer.Length);
            _recordsInBuffer = bytes / _recordSize;
            _bufferIndex = 0;
        }

        /// <summary>
        /// Read a record
        /// </summary>
        /// <returns>a record of type T</returns>
        public T Read() {
            if(_recordsInBuffer == 0)
                return new T(); //EOF
            if(_bufferIndex < _recordsInBuffer) {
                // update positional info
                _recordPosition++;
                return _structBuffer[_bufferIndex++];
            } else {
                // refill the buffer
                FillBuffer();
                return Read();
            }
        }

        /// <summary>
        /// Advances the record position without reading.
        /// </summary>
        public void Next() {
            if(_recordsInBuffer == 0)
                return; // EOF
            else if(_bufferIndex < _recordsInBuffer) {
                _bufferIndex++;
                _recordPosition++;
            } else {
                FillBuffer();
                Next();
            }
        }

        public long FileSize {
            get { return _fileSize; }
        }

        public long FilePosition {
            get { return _recordSize * _recordPosition; }
        }

        public long RecordSize {
            get { return _recordSize; }
        }

        public long RecordPosition {
            get { return _recordPosition; }
        }

        public bool EOF {
            get { return _recordsInBuffer == 0; }
        }

        public void Close() {
            Dispose(true);
        }

        void Dispose(bool disposing) {
            try {
                if(disposing && _fs != null) {
                    _fs.Close();
                }
            } finally {
                if(_fs != null) {
                    _fs = null;
                    _buffer = null;
                    _recordPosition = 0;
                    _bufferIndex = 0;
                    _recordsInBuffer = 0;
                }
                if(_h.IsAllocated) {
                    _h.Free();
                    _structBuffer = null;
                }
            }
        }

        #region IDisposable Members

        public void Dispose() {
            Dispose(true);
        }

        #endregion

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator() {
            while(_recordsInBuffer != 0) {
                yield return Read();
            }
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }

        #endregion

    } // end class

} // end namespace

a utilizar:

using(RecordReader<CPP_STRUCT_DEF> reader = new RecordReader<CPP_STRUCT_DEF>(path)) {
    foreach(CPP_STRUCT_DEF record in reader) {
        // do stuff
    }
}

(bastante nuevo aquí, espero que no era demasiado para publicar ... acaba de pegar en la clase, no cortar los comentarios o cualquier cosa para acortarlo.)

Parece esto no tiene nada que ver con ninguno de C ++ ni de clasificación. Se conoce la estructura lo que más se puede pedir.

Obviamente se necesita un código simple que leerá grupo de bytes que representa una estructura y luego usando BitConverter colocar bytes en campos # C correspondiente ..

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top