Quel est le moyen le plus efficace de structurer les structures C ++ en C #?

Je suis sur le sharepoint commencer à lire des tonnes de fichiers binarys, chacun avec 1000 enregistrements ou plus. De nouveaux fichiers sont ajoutés en permanence, j’écris donc un service Windows pour surveiller les répertoires et traiter les nouveaux fichiers à mesure qu’ils sont reçus. Les fichiers ont été créés avec un programme c ++. J’ai recréé les définitions de structure en c # et je peux lire correctement les données, mais je crains que cela ne finisse par tuer mon application.

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

Je ne pense pas avoir besoin d’utiliser GCHandle car je ne communique pas avec l’application C ++, tout se fait à partir de code managé, mais je ne connais pas de méthode alternative.

Pour votre application particulière, une seule chose vous donnera la réponse définitive: le profiler.

Ceci étant dit, voici les leçons que j’ai apsockets en travaillant avec de grandes solutions PInvoke. Le moyen le plus efficace de rassembler les données est de regrouper les champs qui sont blittables. Cela signifie que le CLR peut simplement faire ce qui revient à une mémoire pour déplacer des données entre du code natif et du code géré. En termes simples, obtenez tous les tableaux et chaînes non intégrés à vos structures. S’ils sont présents dans la structure native, représentez-les avec un IntPtr et regroupez les valeurs à la demande en code managé.

Je n’ai jamais fait la distinction entre l’utilisation de Marshal.PtrToStructure et le fait de déréférencer la valeur avec une API native. C’est probablement quelque chose dans lequel vous devriez investir si PtrToStructure devait être révélé comme un goulot d’étranglement via le profilage.

Pour les grandes hiérarchies, associez-les à la demande ou transposez une structure entière dans un code géré en une seule fois. Je suis le plus souvent confronté à ce problème lorsqu’il s’agit de grandes structures arborescentes. La planification d’un nœud individuel est très rapide si elle est blittable et si elle est performante, elle ne fait que rassembler ce dont vous avez besoin à ce moment-là.

L’utilisation de Marshal.PtrToStructure est plutôt lente. J’ai trouvé l’article suivant très utile sur CodeProject, qui compare (et compare) différentes méthodes de lecture de données binarys:

Lecture rapide de fichiers binarys avec C #

En plus de la réponse complète de JaredPar, vous n’avez pas besoin d’utiliser GCHandle , vous pouvez utiliser du code non sécurisé.

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

Le but de l’ GCHandle / fixed est d’épingler / corriger le segment de mémoire particulier, rendant la mémoire inamovible du sharepoint vue de GC. Si la mémoire était mobile, toute relocalisation rendrait vos pointeurs invalides.

Je ne sais pas quel chemin est plus rapide cependant.

C’est peut-être en dehors des limites de votre question, mais je serais enclin à écrire un petit assemblage en Managed C ++ faisant un fread () ou quelque chose de similaire à lire rapidement dans les structs. Une fois que vous les avez lues, vous pouvez utiliser C # pour faire tout ce dont vous avez besoin avec elles.

voici une petite classe que j’ai faite il y a longtemps en jouant avec des fichiers structurés. c’était la méthode la plus rapide que j’ai pu trouver à ce moment-là, timide pour ne pas devenir dangereuse (c’était ce que j’essayais de remplacer et de maintenir une performance comparable.)

 using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; namespace PersonalUse.IO { public sealed class RecordReader : IDisposable, IEnumerable 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 /// Initializes a new instance of the  class. ///  /// Initializes a new instance of the  class. ///  /// filename to be read public RecordReader(ssortingng filename) : this(filename, DEFAULT_STREAM_BUFFER_SIZE, DEFAULT_RECORD_BUFFER_SIZE) { } ///  /// Initializes a new instance of the  class. ///  /// filename to be read /// buffer size for the underlying , in bytes. public RecordReader(ssortingng filename, int streamBufferSize) : this(filename, streamBufferSize, DEFAULT_RECORD_BUFFER_SIZE) { } ///  /// Initializes a new instance of the  class. ///  /// filename to be read /// buffer size for the underlying , in bytes. /// size of record buffer, in records. public RecordReader(ssortingng 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; } ///  /// Read a record ///  /// a record of type T 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(); } } ///  /// Advances the record position without reading. ///  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 Members public IEnumerator 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 

utiliser:

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

(assez nouveau ici, espérons que ce n’était pas trop à poster … juste collé dans la classe, n’a pas coupé les commentaires ou quoi que ce soit pour le raccourcir.)

Il semble que cela n’ait rien à voir avec C ++ ni le marshalling. Vous connaissez la structure de quoi d’autre avez-vous besoin.

Bien évidemment, vous avez besoin d’un code simple qui lit un groupe d’octets représentant une structure, puis utilise BitConverter pour placer des octets dans les champs C # correspondants.