C#2にC ++の構造体をマーシャリングするための最も効率的な方法は何ですか?

StackOverflow https://stackoverflow.com/questions/878073

質問

私は1000件の以上のレコードを持つバイナリファイルの読み取りトン、それぞれが始まろうとしています。新しいファイルには、私はディレクトリを監視し、それらが受信されると、新しいファイルを処理するWindowsサービスを書いて常に追加されます。ファイルは、C ++プログラムで作成されました。私はC#で構造体の定義を再作成してきましたし、データの罰金を読むことができますが、私はそれをやっている方法は、最終的に自分のアプリケーションを殺すだろうと心配しています。

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 */
    }
}

私は私が実際にC ++アプリケーションと通信していないよので、GCHandleを使用する必要はないと思う、すべてのものは、マネージコードから行われているが、私は別の方法を知りません。

役に立ちましたか?

解決

それをプロフィール:

あなたの特定のアプリケーションのために、一つだけの事はあなたに決定的な答えを与えるだろう。

ここで言われていることは、大きなのPInvokeソリューションと連携しながら、私が学んだ教訓です。データをマーシャリングするための最も効果的な方法は、blittable型のフィールドをマーシャリングすることです。 CLRを意味シンプルネイティブとマネージドコードの間でデータを移動するためのmemcpyになるものを行うことができます。簡単に言えば、あなたの構造のうち、非インライン配列と文字列のすべてを取得します。彼らは天然構造で存在している場合は、のIntPtrでそれらを表現し、マネージコードにオンデマンド値をマーシャリングます。

私が今までに値を間接参照のネイティブAPIを持つ対Marshal.PtrToStructureを使用しての違いをプロファイリングしていません。これはおそらくPtrToStructureプロファイリングを経由して、ボトルネックとして明らかにされなければならない、あなたがに投資すべきものです。

一度にマネージコードに構造全体を引っ張っ対オンデマンドで大規模な階層マーシャルください。私は、ほとんどの大規模なツリー構造を扱うこの問題に遭遇しました。それはblittable型だとパフォーマンスが賢明それだけでその瞬間に何が必要マーシャルに出て働く場合は、個々のノードをマーシャリングすることは非常に高速です。

他のヒント

Marshal.PtrToStructureを使用すると、かなり遅いです。私は非常に有用バイナリデータを読み込むためのさまざまな方法を比較する(およびベンチマーク)さCodeProjectの上の次の記事を見つけます:

  

C#のので読み込みの高速バイナリファイル

JaredParの包括的な答えに加えて、あなたはあなたの代わりに危険なコードを使用することができ、GCHandleを使用する必要はありません。

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

GCHandle / fixed文の全体の目的は、ビューのGCの点から不動メモリを作り、/ピン特定のメモリセグメントを固定することです。メモリが可動した場合は、任意の再配置が無効あなたのポインタをレンダリングします。

わからないその方法はしかし速くなります。

これは、あなたの質問の範囲外かもしれませんが、私は、関数fread()または構造体で読むと同様に速い何かをしたマネージドC ++で少しアセンブリを書くために傾けられます。あなたがそれらを読んで持ったら、あなたは彼らと一緒に必要な他のすべてを行うためにC#を使用することができます。

ここでは構造化されたファイルを再生しながら、私はしばらく前に作られた小さなクラスです。それは私が危険に行くの恥ずかしがり屋の時点で把握できた最速の方法であった(私は交換し、同等の性能を維持しようとしていた何であった。)

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

を使用するには:

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

(かなり新しいここでは、投稿しても過言ではなかった希望が...ちょうどクラスに貼り付け、それを短くするコメントか何かをみじん切りにしませんでした。)

C ++やマーシャリングでもないとは何の関係もありませんようです。あなたが必要なのですか他に何の構造を知っています。

もちろん、あなたは1つの構造体を表すバイトのグループを読み、それに対応するC#のフィールドにバイトを配置するBitConverterを使用して..

する単純なコードが必要です
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top