2009-05-18 9 views
5

मैं कई बाइनरी फाइलों को पढ़ने शुरू कर रहा हूं, प्रत्येक 1000 या अधिक रिकॉर्ड के साथ। नई फाइलें लगातार जोड़ दी जाती हैं इसलिए मैं निर्देशिकाओं की निगरानी करने और नई फाइलों को संसाधित करने के लिए एक विंडोज सेवा लिख ​​रहा हूं। फाइलें सी ++ प्रोग्राम के साथ बनाई गई थीं। मैंने सी # में स्ट्रक्चर परिभाषाओं को फिर से बनाया है और डेटा को ठीक से पढ़ सकता हूं, लेकिन मुझे चिंता है कि जिस तरह से मैं इसे कर रहा हूं वह अंततः मेरे आवेदन को मार देगा।मार्शल सी ++ structs से सी # के लिए सबसे प्रभावी तरीका क्या है?

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

मुझे नहीं लगता कि मैं क्योंकि मैं वास्तव में सी ++ अनुप्रयोग के साथ संवाद स्थापित करने नहीं कर रहा हूँ GCHandle उपयोग करने की आवश्यकता है, सब कुछ प्रबंधित कोड से किया जा रहा है, लेकिन मैं एक वैकल्पिक पद्धति का पता नहीं है।

उत्तर

6

अपने विशेष आवेदन के लिए, केवल एक चीज आपको निश्चित उत्तर देगी: प्रोफाइल करें।

ऐसा कहा जा रहा है कि मैंने बड़े PInvoke समाधानों के साथ काम करते हुए सीखा है। मार्शल डेटा का सबसे प्रभावी तरीका मार्शल फ़ील्ड है जो ब्लिटेबल हैं। मतलब सीएलआर मूल और प्रबंधित कोड के बीच डेटा को स्थानांतरित करने के लिए एक memcpy की मात्रा को सरल कर सकता है। सरल शब्दों में, सभी गैर-इनलाइन सरणी और तारों को अपने ढांचे से बाहर निकालें। यदि वे मूल संरचना में मौजूद हैं, तो उन्हें एक इंटिप्टर के साथ प्रतिनिधित्व करें और प्रबंधित कोड में मांगों पर मूल्यों को मार्शल करें।

मैंने कभी भी मार्शल.प्रेटोस्ट्रक्चर बनाम उपयोग करने के बीच अंतर को प्रोफाइल नहीं किया है, जिसमें मूल एपीआई डीरेंसेंस मान है। यह शायद कुछ ऐसा है जो आपको निवेश करना चाहिए, पीआरटीओस्ट्रक्चर को प्रोफाइलिंग के माध्यम से एक बाधा के रूप में प्रकट किया जाना चाहिए।

बड़ी पदानुक्रमों के लिए मांग पर बनाम मार्शल एक पूरे ढांचे को एक ही समय में प्रबंधित कोड में खींच रहा है। बड़े पेड़ संरचनाओं से निपटने के दौरान मैंने इस मुद्दे में सबसे अधिक भाग लिया है। एक व्यक्तिगत नोड मार्शलिंग बहुत तेजी से होता है यदि यह धब्बेदार और प्रदर्शन के अनुसार होता है तो यह उस क्षण में केवल मार्शल तक काम करता है जो आपको चाहिए।

7

Marshal.PtrToStructure का उपयोग करना धीमा है। मैं CodeProject पर निम्न आलेख जो बहुत उपयोगी तुलना कर रहा है (और बेंचमार्किंग) बाइनरी डेटा पढ़ने के विभिन्न तरीके पाया:

Fast Binary File Reading with C#

+1

धन्यवाद, इस articlt न केवल फ़ाइल संचालकों के बीच मतभेदों को पता चलता है, लेकिन यह भी देता है बाइट टू स्ट्रक्चर रूपांतरण का एक अच्छा उदाहरण। –

1

यह आपके प्रश्न की सीमा से बाहर हो सकता है, लेकिन मैं होगा प्रबंधित सी ++ में एक छोटी सी असेंबली लिखने के इच्छुक जो फ्रेड() या कुछ समान रूप से स्ट्रक्चर में पढ़ने के लिए तेज़ था। एक बार जब आप उन्हें पढ़ लेंगे, तो आप उन सभी चीजों को करने के लिए सी # का उपयोग कर सकते हैं जिनकी आपको आवश्यकता है।

2

जेरेडपार के व्यापक उत्तर के अतिरिक्त, आपको GCHandle का उपयोग करने की आवश्यकता नहीं है, आप इसके बजाय असुरक्षित कोड का उपयोग कर सकते हैं।

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

GCHandle/fixed बयान का पूरा उद्देश्य पिन करने के लिए/विशेष स्मृति खंड को ठीक, स्मृति को देखने के जी सी के दृष्टिकोण से अचल बना रही है। अगर स्मृति चलती थी, तो कोई भी स्थानांतरण आपके पॉइंटर्स को अमान्य प्रदान करेगा।

सुनिश्चित नहीं है कि कौन सा तरीका तेज़ है।

+0

सुझाव के लिए धन्यवाद। मैं जार्रेड की तरह प्रोफाइल करने जा रहा हूं, लेकिन मैं इस विधि का उपयोग कर प्रोफाइल भी करूंगा। – scottm

0

यहां एक छोटी सी कक्षा है जिसे मैंने संरचित फाइलों के साथ खेलते समय थोड़ी देर पहले बनाया था। यह सबसे तेज़ तरीका था जिसे मैं असुरक्षित जाने के शर्मिंदा समय में समझ सकता था (जो कि मैं तुलनीय प्रदर्शन को बदलने और बनाए रखने की कोशिश कर रहा था।)

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

(बहुत यहाँ नया, उम्मीद है कि पोस्ट करने के लिए बहुत ज्यादा ... बस कक्षा में चिपकाया, टिप्पणी या कुछ भी बाहर काट नहीं था नहीं था इसे छोटा करने के लिए।)

0

ऐसा लगता है कि इसका न तो सी ++ और न ही मार्शलिंग के साथ कुछ लेना देना नहीं है। आप संरचना को जानते हैं कि आपको और क्या चाहिए।

जाहिर है आप एक सरल कोड है जो एक struct का प्रतिनिधित्व बाइट्स की समूह पढ़ा जाएगा और उसके बाद सी # क्षेत्रों इसी में बाइट्स जगह BitConverter का उपयोग कर की जरूरत है ..

संबंधित मुद्दे