سؤال

صادفت موقفا فيه ملف كبير جدا أحتاج إلى قراءة البيانات الثنائية من.

وبالتالي، أدركت أن تطبيق BinaryReader الافتراضي في .NET هو بطيء جدا. عند النظر إليها .net عاكس جئت عبر هذا:

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 ميغابايت، لكنه لا يزال بطيئا جدا (29 ثانية في البداية و 22 ثانية مع بلدي FastBinaryReader).

لا يزال نوعا من الحواجز لي لماذا لا يزال يستغرق وقتا طويلا لقراءة مثل هذا الملف الصغير نسبيا. إذا قمت بنسخ الملف من قرص واحد إلى آخر، فإنه يستغرق بضع ثوان فقط، لذلك فإن إنتاجية القرص ليست مشكلة.

قمت بإنطام المزيد من القراءة 32، وما إلى ذلك المكالمات، وانتهى بي الأمر مع هذا الرمز:

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

أي أفكار أخرى حول كيفية جعل هذا أسرع؟ كنت أفكر ربما يمكنني استخدام التنظيم لتعيين الملف بأكمله مباشرة إلى الذاكرة أعلى بعض البنية المخصصة، لأن البيانات خطية وحجم ثابت وتسلسل.

تم حلها: جئت إلى استنتاج مفاده أن التخزين المؤقت للتأخير / المخزن المؤقت. يرجى الاطلاع على الإجابة المقبولة وجبتي الخاصة (مع الحل) أدناه.

هل كانت مفيدة؟

المحلول

عند إجراء مزيلات، تتم قراءة قطع البيانات الكبيرة والكتابة على القرص.

أنت تقرأ الملف بأكمله أربعة بايت في وقت واحد. هذا لا بد أن يكون أبطأ. حتى إذا كان تنفيذ الدفق ذكيا بما فيه الكفاية إلى المخزن المؤقت، فلا يزال لديك ما لا يقل عن 500 ميغابايت / 4 = 131072000 مكالمات API.

أليس من الحكمة قراءة جزء كبير من البيانات، ثم انتقل إليه بالتتابع، وكرر حتى تتم معالجة الملف؟

نصائح أخرى

ركضت في مشكلة أداء مماثلة مع BinaryReader / FileStream، وبعد التنميط، اكتشفت أن المشكلة ليست مع FileStream التخزين المؤقت، ولكن بدلا من ذلك مع هذا الخط:

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

على وجه التحديد، الممتلكات br.BaseStream.Length على FileStream يجعل دعوة نظام بطيئة (نسبيا) للحصول على حجم الملف على كل حلقة. بعد تغيير الرمز إلى هذا:

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

واستخدام حجم المخزن المؤقت المناسب ل FileStream, لقد حققت أداء مماثل ل MemoryStream مثال.

مثيرة للاهتمام، وقراءة الملف بأكمله في مخزن مؤقت ويمارسه في الذاكرة جعل فرقا كبيرا. هذا هو تكلفة الذاكرة، ولكن لدينا الكثير.

هذا يجعلني أعتقد أن التنفيذ المخزن المؤقت (أو المخزن المؤقت لهذه المسألة (أو المخزن المؤقت لهذا المسألة) معيبة، لأنه بغض النظر عن حجم المخزن المؤقت الحجم الذي جربته، لا يزال الأداء امتص.

  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. وهو أمر جيد بما فيه الكفاية الآن.

تحذير واحد قد ترغب في التحقق من ضعف النخلة المعالجة المركزية... على افتراض أن الإندان الصغير ليس كذلك الى حد كبير آمنة (فكر: إيتانيوم وغيرها).

قد ترغب أيضا في معرفة ما إذا كان BufferedStream يجعل أي فرق (لست متأكدا من ذلك سوف).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top