ما هي أفضل طريقة للكتابة إلى ملفات أكثر مما يسمح به kernel في وقت واحد؟

StackOverflow https://stackoverflow.com/questions/3054892

سؤال

لدي ملف ثنائي كبير جدًا وأحتاج إلى إنشاء ملفات منفصلة بناءً على المعرف داخل ملف الإدخال. هناك 146 ملف إخراج وأنا أستخدمه cstdlib و fopen و fwrite. FOPEN_MAX هو 20 ، لذلك لا يمكنني الحفاظ على جميع ملفات الإخراج 146 مفتوحة في نفس الوقت. أريد أيضًا تقليل عدد المرات التي أفتح فيها وأغلق ملف الإخراج.

كيف يمكنني الكتابة إلى ملفات الإخراج بشكل فعال؟

يجب أن أستخدم أيضًا cstdlib مكتبة بسبب الرمز القديم.

يجب أن يكون القابل للتنفيذ أيضًا متوافقًا مع Unix و Windows Cross-Platform.

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

المحلول

زوجين محتملين قد تتخذهما:

  • حافظ على ذاكرة التخزين المؤقت لمقابض ملف الإخراج المفتوحة التي تكون أقل من FOPEN_MAX - إذا كانت الكتابة تحتاج إلى حدوث ملفات مفتوحة بالفعل ، فما عليك سوى القيام بالكتابة. خلاف ذلك ، أغلق أحد المقابض في ذاكرة التخزين المؤقت وفتح ملف الإخراج. إذا تم تجميع بياناتك عمومًا معًا من حيث البيانات الخاصة بمجموعة معينة من الملفات يتم تجميعها معًا في ملف الإدخال ، فيجب أن يعمل هذا بشكل جيد مع سياسة LRU لذاكرة التخزين المؤقت لمواجهة الملف.

  • تعامل مع الإخراج الذي يخزن نفسك بدلاً من السماح للمكتبة بذلك من أجلك: احتفظ بمجموعتك الخاصة من 146 (أو مهما كنت قد تحتاجها) المخازن المؤقتة للإخراج وتخزين الإخراج لها ، وأداء مفتوح/تدفق/إغلاق عند إخراج معين يتم ملء المخزن المؤقت. يمكنك حتى الجمع بين هذا مع النهج أعلاه لتقليل العمليات المفتوحة/الإغلاق حقًا.

فقط تأكد من اختبارك جيدًا لظروف الحافة التي يمكن أن تحدث عند ملء أو ملء المخزن المؤقت للإخراج تقريبًا.

نصائح أخرى

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

إذا لم تتمكن من زيادة Max Fopen_Max بطريقة أو بأخرى ، فيمكنك إنشاء قائمة انتظار بسيطة من الطلبات ثم إغلاق الملفات وإعادة فتحها حسب الحاجة.

يمكنك أيضًا تتبع آخر وقت للكتابة لكل ملف ، ومحاولة الحفاظ على أحدث الملفات المكتوبة.

يبدو الحل واضحًا - افتح ملفات N ، حيث N أقل إلى حد ما من FOPEN_MAX. ثم اقرأ من خلال ملف الإدخال واستخلص محتويات ملفات الإخراج الأولى. ثم أغلق ملفات الإخراج ، وإرجاع الإدخال ، وكرر.

بادئ ذي بدء ، أتمنى أن تكون تسير بالتوازي قدر الإمكان. لا يوجد سبب يمنعك من الكتابة إلى ملفات متعددة في نفس الوقت. أوصي بعمل ما قاله Thomask وطلبات قائمة الانتظار. يمكنك بعد ذلك استخدام بعض مزامنة الخيط للانتظار حتى يتم مسح قائمة الانتظار بأكملها قبل السماح للجولة التالية من الكتابة بالمرور.

لم تذكر ما إذا كان من الضروري الكتابة إلى هذه المخرجات في "الوقت الفعلي" ، أو مقدار البيانات التي يتم كتابتها. مع مراعاة قيودك ، قد يكون أحد الخيارات هو تخزين جميع المخرجات وكتابتها في نهاية تشغيل البرنامج.

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

يمكنك القيام بذلك في خطوتين.

1) اكتب أول 19 معرفات إلى ملف واحد ، معرفات 19 التالية إلى الملف التالي وما إلى ذلك. لذلك تحتاج إلى 8 ملفات إخراج (وملف الإدخال) مفتوحة بالتوازي لهذه الخطوة.

2) لكل ملف تم إنشاؤه ، قم بإنشاء 19 (13 فقط لآخر ملف) ملفات جديدة واكتب المعرفات إليه.

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

أعتقد في معظم الحالات أنه من المفيد فتح الملفات وإغلاقها في كثير من الأحيان.

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

أقترح الحفاظ على std::map أو std::vector من FILE مؤشرات. ال map يتيح لك الوصول إلى مؤشرات الملفات بواسطة معرف. إذا كان نطاق الهوية صغيرًا ، فيمكنك إنشاء ملف vector, ، والحفاظ على العناصر ، واستخدام المعرف كفهرس. سيتيح لك ذلك الحفاظ على الكثير من الملفات مفتوحة في نفس الوقت. احذر مفهوم فساد البيانات.

يتم تعيين حد الملفات المفتوحة المتزامنة بواسطة نظام التشغيل. على سبيل المثال ، إذا كان لنظام التشغيل الخاص بك 10 10 ، فسيكون لديك ترتيبات عند طلب الملف الحادي عشر.

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

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

لقد ذكرت في تعليق أن السرعة كانت مصدر قلق كبير وأن النهج الساذج بطيء للغاية.

هناك بعض الأشياء التي يمكنك البدء في التفكير فيها. أحدهما هو إعادة تنظيم الملف الثنائي في شرائط متسلسلة ، مما يسمح بعمليات متوازية. آخر هو النهج الأقل استخدامًا في مجموعة FileHandle. قد يكون هناك نهج آخر هو الخروج إلى 8 عمليات مختلفة ، كل إخراج إلى 19-20 ملف.

ستكون بعض هذه الأساليب عملية إلى حد ما للكتابة اعتمادًا على المنظمة الثنائية (مجزأة للغاية مقابل متسلسلة للغاية).

القيد الرئيسي هو حجم بياناتك الثنائية. هل هو أكبر من ذاكرة التخزين المؤقت؟ أكبر من الذاكرة؟ تدفق من سطح الشريط؟ باستمرار الخروج من دفق المستشعر والوجود فقط كـ "ملف" في الذاكرة؟ كل من هذه يقدم استراتيجية تحسين مختلفة ...

سؤال آخر هو أنماط الاستخدام. هل تقوم بعمل مكتبات Spike من حين لآخر ، أم أن لديك أجزاء ضخمة مكتوبة عدة مرات فقط؟ التي تحدد فعالية استراتيجيات التخزين المؤقت/الترحيل المختلفة في FileHandles.

على افتراض أنك على نظام *nix ، فإن الحد هو في كل عملية ، وليس على مستوى النظام. وهذا يعني أنه يمكنك تشغيل عمليات متعددة ، كل مسؤول عن مجموعة فرعية من المعرف الذي تقوم بتصفية. يمكن أن يحتفظ كل منها داخل FOPEN_MAX لعمليته.

يمكن أن يكون لديك عملية الوالدين قراءة ملف الإدخال ثم إرسال البيانات إلى عمليات "الكتابة" المختلفة من خلال ملفات الأنابيب الخاصة.

استراتيجية "FILE FILE OPES":

لتحقيق الحد الأدنى لعدد الملفات التي يتم فتحها وإغلاقها ، سيتعين عليك قراءة المدخلات عدة مرات. في كل مرة ، يمكنك اختيار مجموعة فرعية من المعرفات التي تحتاج إلى الفرز ، واستخراج تلك السجلات فقط في ملفات الإخراج.

رمز كاذب لكل موضوع:

  1. قم بتشغيل الملف ، وجمع جميع المعرفات الفريدة.
  2. fseek() العودة إلى بداية المدخلات.
  3. لكل مجموعة من 19 معرفات:
    1. افتح ملفًا لكل معرف.
    2. قم بتشغيل ملف الإدخال ، وإلحاق السجلات المطابقة بملف الإخراج المقابل.
    3. أغلق هذه المجموعة من 19 ملف الإخراج.
    4. fseek() إلى بداية المدخلات.

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

استراتيجية "أقل من عمليات الملف"

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

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

يمكنك تغيير وحدة ملفات الإخراج Flushing إلى القرص. ربما لديك كمية كافية من ذاكرة الوصول العشوائي لجمع 200 صفحة في وقت واحد ، لكل ملف إخراج؟

أشياء يجب توخي الحذر بشأن:

  • هل محاذاة صفحة البيانات الخاصة بك؟ إذا لم يكن الأمر كذلك ، يجب أن تكون ذكيًا في قراءة "الصفحة التالية".
  • تأكد من أنه ليس لديك موضوعان fwrite()"على ملف الإخراج نفسه في نفس الوقت. إذا حدث ذلك ، فقد تفسد إحدى الصفحات.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top