سؤال

بالنظر إلى ملف ضخم حقًا (ربما أكثر من 4 جيجا بايت) على القرص ، أريد مسحًا من خلال هذا الملف وحساب أوقات نمط ثنائي معين.

فكرتي هي:

  1. استخدم ملف الذاكرة (CreateFileMap أو Boost Meded_file) لتحميل الملف إلى الذاكرة الظاهرية.

  2. لكل 100 ميغابايت من الذاكرة المعينة ، قم بإنشاء مؤشر ترابط واحد لمسح النتيجة وحسابها.

هل هذا ممكن؟ هل هناك أي طريقة أفضل للقيام بذلك؟

تحديث:
سيكون الملف الذي تم تعيينه للذاكرة خيارًا جيدًا ، يمكن معالجته للمسح من خلال ملف 1.6 جيجابايت في غضون 11 ثانية.

شكرًا.

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

المحلول

لن تجعل Multithreading هذا أبطأ إلا إذا كنت تريد مسح ملفات متعددة باستخدام كل محرك أقراص ثابت مختلف. وإلا فإنك ستسعى فقط.

لقد كتبت وظيفة اختبار بسيطة باستخدام ملفات الذاكرة المعينة ، مع سلسلة واحدة استغرق ملف 1.4 جيجابايت حوالي 20 ثانية للمسح الضوئي. مع اثنين من المواضيع ، يأخذ كل منهما نصف الملف (حتى 1 ميجابايت قطع إلى مؤشر ترابط واحد ، غريب إلى الآخر) ، استغرق الأمر أكثر من 80 ثانية.

  • 1 الموضوع: 20015 مللي ثانية
  • 2 المواضيع: 83985 مللي ثانية

هذا صحيح ، كان 2 خيوط أربعة مرات أبطأ من 1 موضوع!

فيما يلي الرمز الذي استخدمته ، هذا هو الإصدار المفرد المسلسل ، لقد استخدمت نمط مسح بايت 1 ، وبالتالي فإن الكود لتحديد المواقع التي لم يتم اختبار الحدود الخريطة.

HRESULT ScanForPattern(LPCTSTR pszFilename, LPBYTE pbPattern, UINT cbPattern, LONGLONG * pcFound)
{
   HRESULT hr = S_OK;

   *pcFound = 0;
   if ( ! pbPattern || ! cbPattern)
      return E_INVALIDARG;

   //  Open the file
   //
   HANDLE hf = CreateFile(pszFilename,
                          GENERIC_READ,
                          FILE_SHARE_READ, NULL,
                          OPEN_EXISTING,
                          FILE_FLAG_SEQUENTIAL_SCAN,
                          NULL);

   if (INVALID_HANDLE_VALUE == hf)
      {
      hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
      // catch an open file that exists but is in use
      if (ERROR_SHARING_VIOLATION == GetLastError())
         hr = HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION);
      return hr;
      }

   // get the file length
   //
   ULARGE_INTEGER  uli;
   uli.LowPart = GetFileSize(hf, &uli.HighPart);
   LONGLONG cbFileSize = uli.QuadPart;
   if (0 == cbFileSize)
      {
      CloseHandle (hf);
      return S_OK;
      }

   const LONGLONG cbStride = 1 * 1024 * 1024; // 1 MB stride.
   LONGLONG cFound  = 0;
   LPBYTE   pbGap = (LPBYTE) malloc(cbPattern * 2);

   //  Create a mapping of the file.
   //
   HANDLE hmap = CreateFileMapping(hf, NULL, PAGE_READONLY, 0, 0, NULL);
   if (NULL != hmap)
      {
      for (LONGLONG ix = 0; ix < cbFileSize; ix += cbStride)
         {
         uli.QuadPart = ix;
         UINT cbMap = (UINT) min(cbFileSize - ix, cbStride);
         LPCBYTE pb = (LPCBYTE) MapViewOfFile(hmap, FILE_MAP_READ, uli.HighPart, uli.LowPart, cbMap);
         if ( ! pb)
            {
            hr = HRESULT_FROM_WIN32(GetLastError());
            break;
            }
         // handle pattern scanning over the gap.
         if (cbPattern > 1 && ix > 0)
            {
            CopyMemory(pbGap + cbPattern - 1, &pb[0], cbPattern - 1);
            for (UINT ii = 1; ii < cbPattern; ++ii)
               {
               if (pb[ii] == pbPattern[0] && 0 == memcmp(&pb[ii], pbPattern, cbPattern))
                  {
                  ++cFound; 
                  // advance by cbPattern-1 to avoid detecting overlapping patterns
                  }
               }
            }

         for (UINT ii = 0; ii < cbMap - cbPattern + 1; ++ii)
            {
            if (pb[ii] == pbPattern[0] && 
                ((cbPattern == 1) || 0 == memcmp(&pb[ii], pbPattern, cbPattern)))
               {
               ++cFound; 
               // advance by cbPattern-1 to avoid detecting overlapping patterns
               }
            }
         if (cbPattern > 1 && cbMap >= cbPattern)
            {
            // save end of the view in our gap buffer so we can detect map-straddling patterns
            CopyMemory(pbGap, &pb[cbMap - cbPattern + 1], cbPattern - 1);
            }
         UnmapViewOfFile(pb);
         }

      CloseHandle (hmap);
      }
   CloseHandle (hf);

   *pcFound = cFound;
   return hr;
}

نصائح أخرى

من المحتمل أن يؤدي إنشاء 20 مؤشر ترابط ، على ما يفترض أن يتعامل مع حوالي 100 ميغابايت من الملف فقط ، حيث سيتعين على HD القراءة من عدة أماكن غير ذات صلة في نفس الوقت.

يكون أداء HD في ذروته عندما يقرأ البيانات المتسلسلة. لذا ، بافتراض أن ملفك الضخم ليس مجزأًا ، فمن المحتمل أن يكون أفضل ما يجب فعله هو استخدام مؤشر ترابط واحد فقط وقراءة من البداية إلى النهاية في أجزاء قليلة (4) ميغابايت.

ولكن ماذا أعرف. أنظمة الملفات واختداءات التخزين المؤقت معقدة. قم ببعض الاختبارات وشاهد ما هو أفضل.

على الرغم من أنه يمكنك استخدام تعيين الذاكرة ، إلا أنه ليس عليك ذلك. إذا قرأت الملف بالتسلسل في قطع صغيرة ، على سبيل المثال 1 ميغابايت لكل منهما ، فلن يكون الملف موجودًا أبدًا في الذاكرة مرة واحدة.

إذا كان رمز البحث الخاص بك أبطأ بالفعل من القرص الثابت ، فلا يزال بإمكانك تسليم القطع إلى مؤشرات ترابط العمال إذا أردت.

سيكون لدي مؤشر ترابط واحد قراءة الملف (ربما كدفق) في صفيف ولديه عملية مؤشر ترابط أخرى. لن أتعرض لخريطة عدة في وقت واحد بسبب تسعى القرص. من المحتمل أن يكون لدي دليل يدوي لأخبر موضوعي عندما يكون التالي؟ البايتات جاهزة للمعالجة. على افتراض أن رمز العملية الخاص بك أسرع ، فإن HDD سيكون لدي 2 مخازن مؤقتة ، واحدة لملءها والآخر للمعالجة والتبديل بينهما في كل مرة.

سأذهب مع موضوع واحد فقط ، ليس فقط لمشكلات أداء عالية الدقة ، ولكن لأنه قد تواجه مشكلة في إدارة الآثار الجانبية عند تقسيم ملفك: ماذا لو كان هناك حدوث نمطك حيث تقسم ملفك؟

يتمتع باستخدام ملف تم تعيينه ذاكرة فائدة إضافية تتمثل في تجنب نسخة من ذاكرة ذاكرة التخزين المؤقت لنظام الملفات إلى ذاكرة التطبيق (المدارة) إذا كنت تستخدم عرضًا للقراءة فقط (على الرغم من أنه يتعين عليك استخدام مؤشرات البايت* ثم للوصول إلى الذاكرة). وبدلاً من إنشاء العديد من مؤشرات الترابط ، استخدم مؤشر ترابط واحد للمسح الضوئي بالتسلسل من خلال الملف باستخدام طرق عرض ذاكرة 100 ميجابايت على سبيل المثال في الملف (لا تقم بتخطيط الملف بأكمله في مساحة عنوان العملية في وقت واحد).

استكشف تيم براي (وقراءه) هذا بتعمق في مشروع مكتشف واسع و مكتشف واسعة 2. النتائج القياسية أظهر أن التطبيقات المتعددة التي تم تربيتها يمكن أن تتفوق على حل واحد على خادم شمس متعدد الأوساخ ضخمة. على أجهزة الكمبيوتر المعتادة ، لن تكسبك MultiTreading كثيرًا ، إن وجد.

أود أن أفعل ذلك بقراءات غير متزامنة في مخزن مؤقت مزدوج. لذلك عندما تتم قراءة عازلة واحدة من الملف ، ابدأ في قراءة المخزن المؤقت التالي أثناء مسح المخزن المؤقت الأول. هذا يعني أنك تقوم بعمل وحدة المعالجة المركزية و IO بالتوازي. ميزة أخرى هي أنه سيكون لديك دائمًا بيانات حول حدود البيانات. ومع ذلك ، لا أعرف ما إذا كان التخزين المؤقت المزدوج ممكنًا مع الملفات المعينة للذاكرة.

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