كيفية تجنب نفاد الذاكرة في تطبيق استخدام ذاكرة عالية؟ C / C ++

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

  •  09-09-2019
  •  | 
  •  

سؤال

لقد كتبت محول يأخذ ملفات OpenStreetMap XML وتحويلها إلى تنسيق تقديم وقت تشغيل ثنائي وهو عادة حوالي 10٪ من الحجم الأصلي. أحجام ملفات الإدخال عادة ما تكون 3 جيجابايت وأكبر. لا يتم تحميل ملفات الإدخال في الذاكرة مرة واحدة في وقت واحد، ولكن يتم تدويتها حيث يتم جمع النقاط والبوليات، ثم يتم تشغيل BSP عليها والملف هو الإخراج. في الآونة الأخيرة على الملفات الكبيرة التي ينفد من الذاكرة ويموت (يحتوي المرء المعني النقاط على 14 مليون دولار و 100 مليون بولاية). عادة ما يستخدم برنامجي حوالي 1 جيجابايت إلى 1.2 جيجابايت من ذاكرة الوصول العشوائي عندما يحدث هذا. لقد حاولت زيادة الذاكرة الظاهرية من 2 إلى 8GB (على XP) ولكن هذا التغيير لم يحدث أي تأثير. أيضا، نظرا لأن هذا الرمز مفتوح المصدر، أود الحصول على عمله بغض النظر عن ذاكرة الوصول العشوائي المتاحة (وإن كان أبطأ)، فهو يعمل على Windows و Linux و Mac.

ما هي التقنيات التي يمكنني استخدامها لتجنب وجودها نفاد الذاكرة؟ معالجة البيانات في مجموعات فرعية أصغر ثم دمج النتائج النهائية؟ باستخدام نوع الذاكرة الافتراضية الخاصة بي من المعالج؟ أي أفكار أخرى؟

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

المحلول

أولا، على نظام 32 بت، ستقتصر دائما على 4 جيجابايت من الذاكرة، بغض النظر عن إعدادات Pheraphile. (ولأولئك، سيكون فقط 2 جيجابايت متاحا لعمليتك على Windows. عند Linux، لديك عادة حوالي 3GB المتاحة)

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

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

ثالثا، قسم المشكلة. لا تقوم بمعالجة DataSet بأكملها مرة واحدة، ولكن حاول التحميل والعملية فقط قسم صغير في كل مرة.

نصائح أخرى

هل راجعت للتأكد من أنك لا تسريب الذاكرة في أي مكان؟

نظرا لأن برنامجك محمول لينكس، أقترح تشغيله تحت Valgrind للتأكد.

يبدو أنك تفعل بالفعل ساكس نهج قائم على معالجة XML (تحميل XML كما تذهب بدلا من الكل مرة واحدة).

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

يمكنك في بعض الأحيان تمديد الذاكرة عبر استخدام القرص الصلب بدلا من ذلك عند الحاجة في خوارزمية الخاص بك.

إذا لم تتمكن من تقسيم خوارزميةك، فربما تريد شيئا مثل ذاكرة تعيين الملفات.

في أسوأ الحالات، يمكنك محاولة استخدام شيء مثل Virtualalloc. إذا كنت على نظام Windows. إذا كنت على نظام 32 بت، فيمكنك محاولة استخدام شيء مثل ملحق العناوين الفيزيائية (PAE).

يمكنك أيضا التفكير في وضع حدود المدخلات للبرنامج الخاص بك، ولديها واحدة مختلفة عن أنظمة 32 بت و 64 بت.

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

على افتراض أنك تستخدم نظام التشغيل Windows XP، إذا كنت فقط فوق حد ذاكرتك فقط ولا ترغب في إعادة صياغة الكود كما هو مقترح أعلاه، يمكنك إضافة رمز التبديل / 3GB الخاص بك boot.ini. قم بملف وبعد ذلك مجرد وضع مفتاح رابط للحصول على ذاكرة 1 جيجابايت إضافية.

عليك أن تفهم أن الذاكرة الظاهرية مختلفة عن "ذاكرة الوصول العشوائي" في أن كمية الذاكرة الافتراضية التي تستخدمها هي المبلغ الإجمالي الذي قمت باحفظه، في حين أن الذاكرة الحقيقية (في Windows Set Working Set) هي ذاكرة تعديل أو مغلق بالفعل.

كما أشار شخص آخر، على منصات Windows 32 بت، فإن الحد الأقصى على الذاكرة الظاهرية هو 2 غيغابايت إلا إذا قمت بتعيين العلم الخاص لمدة 3 غيغابايت ويمكنك التأكد من أن جميع المؤشرات في كلا من التعليمات البرمجية وأي مكتبات تستخدمها استخدم فقط مؤشرات غير موقعة.

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

انتقدت إلى الجدار 32 بت في ويندوز، ولكن ليس لديها خبرة في العمل حول هذه القيود في Linux، لذا فقد تحدثت فقط عن جانب Windows من الأشياء.

على 32 بت XP، فإن مساحة عنوان البرنامج القصوى هو 2GB. ثم لديك تجزئة بسبب تحميل DLL والسائقين في مساحة عنوانك. أخيرا، لديك مشكلة في تجزئة الكومة الخاص بك.

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

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

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

الصبي، لا يحتاج 64 بت فقط صوت أجمل بكثير من الخيارات الأخرى؟

كيف تخصص الذاكرة للحصول على النقاط؟ هل تخصص نقطة واحدة في وقت واحد (مثل PT = نقطة جديدة). ثم اعتمادا على حجم النقطة، قد تضيع بعض الذاكرة. على سبيل المثال، يتم تخصيص ذاكرة Windows في مضاعفات 16 بايت، لذا حتى إذا طرحت محاولة تخصيص 1 بايت، فسوف تخصيص نظام التشغيل بالفعل 16 بايت.

إذا كانت هذه هي الحالة، فقد يساعد استخدام مخصص الذاكرة. يمكنك إجراء فحص سريع باستخدام STL مخصص. (فوق تحميل المشغل الجديد لفئة النقطة واستخدم STL مخصصا لتخصيص الذاكرة بدلا من "malloc" أو مشغل جديد افتراضي).

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

إذا كنت لا ترغب في قضاء بعض الوقت في تحسين استخدام الذاكرة، فلماذا لا تحاول جامع القمامة المحافظب إنه استبدال المكونات الإضافية ل Malloc () / جديد ومجاني (). في الواقع، مجاني () غير مرجع، حتى تتمكن من إزالة هذه المكالمات من برنامجك. إذا كنت تفعل ذلك، بدلا من ذلك، يمكنك تحسين برنامجك وإدارة مجموعة من الذاكرة كما هو مقترح سابقا، فسوف ينتهي بك الأمر بعمل الكثير من العمل الذي تقوم به CGC بالفعل لك.

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

يبدو أنك تفعل النص إلى المحادثة الثنائية فلماذا تحتاج إلى الحصول على البيانات بأكملها في الذاكرة؟
لا يمكنك فقط قراءة بدائية من TXT (XML) ثم حفظها إلى BinaryStream؟

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

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

الآن هذا يبدو سهلا، أليس كذلك؟ (سعيد ليس لدي للقيام بذلك :))

لا تحتاج إلى التبديل إلى آلات 64 بت، ولا تحتاج إلى معظم الأشياء التي اقترحها الآخرون. ما تحتاجه هو خوارزمية أكثر فاعن.

فيما يلي بعض الأشياء التي يمكنك القيام بها للمساعدة في هذا الموقف:

  • إذا كنت على Windows، استخدم خرائط الملفات (عينة من الرموز). سيعطي ذلك إمكانية الوصول إلى الملف عبر مؤشر مخزن مؤقت واحد كما لو كنت تقرأ الملف بأكمله في الذاكرة، فقط دون القيام بذلك بالفعل. الإصدارات الحديثة من Linux Kernel لها آلية مماثلة.
  • إذا استطعت، ويبدو أنه يمكنك، فقم بمسح الملف بالتتابع وتجنب إنشاء دوم في الذاكرة. هذا سوف يقلل إلى حد كبير وقت التحميل الخاص بك وكذلك متطلبات الذاكرة.
  • استخدام الذاكرة المجمعة! من المحتمل أن يكون لديك العديد من الأشياء الصغيرة، مثل العقد والنقاط وأات. استخدم ذاكرة مجمعة للمساعدة (أفترض أنك تستخدم لغة غير مدارة. ابحث عن تخصيص مجمع وحمامات الذاكرة).
  • إذا كنت تستخدم لغة مدارة، على الأقل نقل هذا الجزء المحدد إلى لغة غير مدارة والتحكم في الذاكرة وقراءة الملفات. اللغات المدارة لديها علامات غير تافهة على حد سواء في بصمة الذاكرة والأداء. (نعم، أعرف أن هذا هو الموسومة "C ++" ...)
  • محاولة تصميم خوارزمية قائمة، حيث تقرأ ومعالجتها فقط الحد الأدنى من البيانات في وقت واحد، لذلك ستذهب متطلبات الذاكرة الخاصة بك إلى أسفل.

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

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

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

هذا سؤال قديم ولكن، منذ أن فعلت مؤخرا نفس الشيء ....

لا توجد إجابة بسيطة. في عالم مثالي، تستخدم جهازا مع مساحة عنوان ضخمة (أي 64 بت)، وكميات هائلة من الذاكرة الفعلية. مساحة العنوان الضخمة وحدها ليست كافية أو سوف سحق فقط. في هذه الحالة تحليل ملف XML إلى قاعدة بيانات، ومع الاستعلامات المناسبة، اسحب ما تحتاجه. من المحتمل جدا أن هذا ما يفعله OSM نفسه (أعتقد أن العالم حوالي 330 جيجابايت).

في الواقع، ما زلت أستخدم XP 32BIT لأسباب من النفعية.

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

الطريقة الوحيدة التي وجدتها لجعلها تعمل جميعها في بصمة صغيرة على آلة 32 بت هي التفكير بعناية فائقة حول ما كنت أفعل وما هو مطلوب عند وكسر المهمة في قطع. فعالة الذاكرة (لا تستخدم أبدا أكثر من 100 ميجابايت) ولكن ليس سريعا بشكل كبير، ولكن بعد ذلك لا يهم - كم مرة يجب على المرء تحليل بيانات XML؟

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