2009-08-06 19 views
22

में मैं एक ऐसी स्थिति में आया जहां मेरे पास एक बहुत बड़ी फ़ाइल है जिसे मुझे बाइनरी डेटा पढ़ने की आवश्यकता है।तेज (असुरक्षित) BETReader .NET

नतीजतन, मुझे एहसास हुआ कि .NET में डिफ़ॉल्ट बाइनरी रीडर कार्यान्वयन बहुत धीमा है। .NET Reflector साथ इसे देख करने पर मैं इस भर में आया था:

public virtual int ReadInt32() 
{ 
    if (this.m_isMemoryStream) 
    { 
     MemoryStream stream = this.m_stream as MemoryStream; 
     return stream.InternalReadInt32(); 
    } 
    this.FillBuffer(4); 
    return (((this.m_buffer[0] | (this.m_buffer[1] << 8)) | (this.m_buffer[2] << 0x10)) | (this.m_buffer[3] << 0x18)); 
} 

कौन सा मुझे अत्यंत अक्षम हमलों, कैसे कंप्यूटर 32 बिट मूल्यों के साथ काम करने के लिए के बाद से 32 बिट सीपीयू आविष्कार किया गया था डिजाइन किए गए थे पर सोच।

तो मैं इस तरह के बजाय के रूप में अपने खुद के (असुरक्षित) FastBinaryReader कोड के साथ वर्ग बनाया:

public unsafe class FastBinaryReader :IDisposable 
{ 
    private static byte[] buffer = new byte[50]; 
    //private Stream baseStream; 

    public Stream BaseStream { get; private set; } 
    public FastBinaryReader(Stream input) 
    { 
     BaseStream = input; 
    } 


    public int ReadInt32() 
    { 
     BaseStream.Read(buffer, 0, 4); 

     fixed (byte* numRef = &(buffer[0])) 
     { 
      return *(((int*)numRef)); 
     } 
    } 
... 
} 

कौन सा बहुत तेजी से है - मैं समय 5-7 सेकंड बंद दाढ़ी बनाने के लिए प्रबंधित यह एक पढ़ने के लिए ले लिया 500   एमबी फ़ाइल, लेकिन यह अभी भी काफी धीमी है (शुरुआत में 2 9 सेकंड और मेरे FastBinaryReader के साथ ~ 22 सेकंड)।

यह अभी भी मुझे परेशान करता है कि यह अपेक्षाकृत छोटी फ़ाइल को पढ़ने के लिए इतना लंबा क्यों लगता है। अगर मैं फ़ाइल को एक डिस्क से दूसरी डिस्क में कॉपी करता हूं तो इसमें केवल कुछ सेकंड लगते हैं, इसलिए डिस्क थ्रूपुट कोई समस्या नहीं है।

मैं आगे ReadInt32 inlined, आदि कॉल, और मैं इस कोड के साथ समाप्त हो गया:

using (var br = new FastBinaryReader(new FileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000, FileOptions.SequentialScan))) 

    while (br.BaseStream.Position < br.BaseStream.Length) 
    { 
     var doc = DocumentData.Deserialize(br); 
     docData[doc.InternalId] = doc; 
    } 
} 

public static DocumentData Deserialize(FastBinaryReader reader) 
    { 
     byte[] buffer = new byte[4 + 4 + 8 + 4 + 4 + 1 + 4]; 
     reader.BaseStream.Read(buffer, 0, buffer.Length); 

     DocumentData data = new DocumentData(); 
     fixed (byte* numRef = &(buffer[0])) 
     { 
      data.InternalId = *((int*)&(numRef[0])); 
      data.b = *((int*)&(numRef[4])); 
      data.c = *((long*)&(numRef[8])); 
      data.d = *((float*)&(numRef[16])); 
      data.e = *((float*)&(numRef[20])); 
      data.f = numRef[24]; 
      data.g = *((int*)&(numRef[25])); 
     } 
     return data; 
    } 

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

सॉल्व: मैं इस निष्कर्ष पर पहुंचा कि फाइलस्ट्रीम के बफरिंग/बुफर्डस्ट्रीम त्रुटिपूर्ण हैं। कृपया स्वीकार किए गए उत्तर और मेरे स्वयं के उत्तर (समाधान के साथ) देखें।

+0

यह सहायक हो सकता है: http://stackoverflow.com/questions/19558435/what-is-the-best-buffer-size-when-using-binaryreader-to-read-big-files-1gb/19837238? noredirect = 1 # 19837238 –

उत्तर

9

जब आप फ़ाइलकॉपी करते हैं, तो डेटा के बड़े भाग डिस्क पर पढ़े जाते हैं और लिखे जाते हैं।

आप एक समय में पूरी फ़ाइल चार बाइट पढ़ रहे हैं। यह धीमा होने के लिए बाध्य है। भले ही स्ट्रीम कार्यान्वयन बफर करने के लिए पर्याप्त स्मार्ट है, फिर भी आपके पास कम से कम 500   एमबी/4 = 131072000 एपीआई कॉल हैं।

क्या डेटा के बड़े हिस्से को पढ़ने के लिए और अधिक बुद्धिमान नहीं है, और उसके बाद अनुक्रमिक रूप से इसके माध्यम से जाएं, और फ़ाइल को संसाधित होने तक दोहराएं?

+1

फ़ाइलस्ट्रीम कन्स्ट्रक्टर में एक पैरामीटर है जो बफर आकार निर्दिष्ट करता है, इसलिए पढ़ना वास्तव में भाग में किया जाता है। मैंने बफर आकार के लिए विभिन्न मूल्यों की कोशिश की, लेकिन इसमें कोई बड़ा सुधार नहीं हुआ। अतिरिक्त बड़े बफर आकार वास्तव में मेरे माप में प्रदर्शन को चोट पहुंचाते हैं। – andreialecu

+0

अभी भी आप 'ReadInt32' पर कॉल की विशाल संख्या कर रहे हैं। बस इसे स्मृति के लगातार टुकड़े से प्राप्त करना बहुत तेज़ होगा। – Toad

+0

कृपया प्रश्न को फिर से पढ़ें, मैं वास्तविक कार्यान्वयन में ReadInt32 का उपयोग नहीं कर रहा हूं, प्रति ऑब्जेक्ट केवल 1 पढ़ा गया है, और सभी रूपांतरणों को रेखांकित किया गया है, कोड के अंतिम दो ब्लॉक देखें। – andreialecu

5

एक चेतावनी; आप अपने CPU's endianness को दोबारा जांचना चाहेंगे ... मानते हैं कि थोड़ा-एंडियन काफी सुरक्षित (सोचें: Itanium आदि)।

आप यह भी देखना चाहते हैं कि BufferedStream कोई फर्क पड़ता है (मुझे यकीन नहीं है कि यह होगा)।

+0

हाँ, मुझे अंतहीन मुद्दों के बारे में पता है, लेकिन यह एक मालिकाना आवेदन है जहां मेरे पास तैनाती पर पूर्ण नियंत्रण है। BufferedStream के बारे में, मेरी समझ से फ़ाइलस्ट्रीम पहले से ही buffered है, तो यह सिर्फ एक अनावश्यक मध्यस्थ बफर जोड़ देगा। पीएस .: मैं इस प्रोजेक्ट में अपनी प्रोटोबफ लाइब्रेरी का भी उपयोग कर रहा हूं, इसके लिए बहुत सारे धन्यवाद :) – andreialecu

+3

मैंने अभी एक लपेटकर बुफर्डस्ट्रीम के साथ एक नया माप बनाया है, और अनुमानित रूप से, इसमें कोई फर्क नहीं पड़ता है। – andreialecu

9

दिलचस्प, पूरी फ़ाइल को एक बफर में पढ़ना और स्मृति में इसके माध्यम से जाना एक बड़ा अंतर बना। यह स्मृति की कीमत पर है, लेकिन हमारे पास बहुत कुछ है।

इससे मुझे लगता है कि फ़ाइलस्ट्रीम (या उस मामले के लिए बुफर्डस्ट्रीम का) बफर कार्यान्वयन त्रुटिपूर्ण है, क्योंकि इससे कोई फर्क नहीं पड़ता कि आकार बफर मैंने कोशिश की, प्रदर्शन अभी भी चूसा गया।

using (var br = new FileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000, FileOptions.SequentialScan)) 
    { 
     byte[] buffer = new byte[br.Length]; 
     br.Read(buffer, 0, buffer.Length); 
     using (var memoryStream = new MemoryStream(buffer)) 
     { 
      while (memoryStream.Position < memoryStream.Length) 
      { 
       var doc = DocumentData.Deserialize(memoryStream); 
       docData[doc.InternalId] = doc; 
      } 
     } 
    } 

2-5 सेकंड के लिए नीचे 22 से अब कौन सा अब के लिए काफी अच्छा है (डिस्क कैश मेरा अनुमान है कि पर निर्भर करता है)।

+0

तो मेरा जवाब दोषपूर्ण नहीं था; ^) – Toad

+3

धन्यवाद। लेकिन वास्तव में .NET के बफर कार्यान्वयन के साथ एक समस्या है, क्योंकि मैंने फ़ाइल के जितना बड़ा बफर आकार (जो मध्यवर्ती मेमोरीस्ट्रीम के बराबर होना चाहिए) की कोशिश की, और यह अभी भी प्रदर्शन के अनुसार चूस गया। सिद्धांत रूप में आपका सुझाव अनावश्यक होना चाहिए था, लेकिन व्यवहार में - जैकपॉट। – andreialecu

+6

आप बस var buffer = file.ReadAllBytes (cacheFilePath) कह सकते हैं; कुछ कोड बचाओ और यह बहुत तेज़ है – gjvdkamp

16

मैं BinaryReader/FileStream के साथ एक इसी तरह के प्रदर्शन मुद्दे में भाग गया, और रूपरेखा के बाद, मुझे पता चला है कि समस्या FileStream बफरिंग के साथ नहीं है, लेकिन इसके बजाय इस लाइन के साथ:

while (br.BaseStream.Position < br.BaseStream.Length) { 

विशेष रूप से, संपत्ति br.BaseStream.LengthFileStream पर प्रत्येक लूप पर फ़ाइल आकार प्राप्त करने के लिए एक (अपेक्षाकृत) धीमी प्रणाली कॉल करता है। यह करने के लिए कोड बदलने के बाद:

long length = br.BaseStream.Length; 
while (br.BaseStream.Position < length) { 

और FileStream के लिए एक उपयुक्त बफर आकार का उपयोग करते हुए, मैं MemoryStream उदाहरण की तरह प्रदर्शन हासिल की।

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