C مقابل C ++ للأداء في تخصيص الذاكرة
-
22-09-2019 - |
سؤال
أخطط للمشاركة في تطوير رمز مكتوب بلغة C لتحليل Monte Carlo للمشاكل المعقدة. تخصص هذه الرموز صفائف بيانات ضخمة في الذاكرة لتسريع أدائها ، وبالتالي اختار مؤلف الكود C بدلاً من C ++ يدعي أنه يمكن للمرء أن يصنع رمزًا أسرع وأكثر موثوقية (فيما يتعلق بتسربات الذاكرة) مع C.
هل توافق على ذلك؟ ماذا سيكون اختيارك ، إذا كنت بحاجة إلى تخزين 4-16 جيجابايت من صفائف البيانات في الذاكرة أثناء الحساب؟
المحلول
بالتأكيد C ++. بشكل افتراضي ، لا يوجد فرق كبير بين الاثنين ، لكن يوفر C ++ بضعة أشياء لا:
- المنشئون/المدمرين. هذه تتيح لك أتمتة معظم إدارة الذاكرة ، وتحسين الموثوقية.
- مخصصات لكل فئة. هذه تتيح لك تحسين التخصيص بناءً على كيفية تصميم و/أو استخدام كائنات معينة. يمكن أن يكون هذا مفيدًا بشكل خاص إذا كنت بحاجة إلى عدد كبير من الكائنات الصغيرة (لإعطاء مثال واضح).
خلاصة القول هي أنه في هذا الصدد ، لا يوفر C أي إمكانية على الإطلاق لميزة على C ++. في أسوأ الحالات ، يمكنك أن تفعل نفس الأشياء بالضبط بنفس الطريقة.
نصائح أخرى
هناك ميزة واحدة من C99 غائبة عن C ++ والتي من المحتمل أن تعطي مكاسب كبيرة في سرعة الرمز الثقيل الذي يربط الأرقام ، وهي الكلمة الرئيسية restrict
. إذا كان بإمكانك استخدام برنامج التحويل البرمجي C ++ الذي يدعمه ، فعليك أن يكون لديك أداة إضافية في المجموعة عندما يتعلق الأمر بالتحسين. إنه مكسب محتمل فقط ، على الرغم من ذلك: يمكن أن يسمح الإضفاء الطويلة الكافية على التحسينات نفسها كما restrict
و اكثر. كما أنه لا علاقة له بتخصيص الذاكرة.
إذا كان مؤلف الكود يمكن أن يوضح اختلاف الأداء بين كود C و C ++ الذي يخصص صفيف 4-16 جيجابايت ، فإن (أ) فوجئت ، لكن حسنًا ، هناك فرق ، و (ب) كم عددهم مرات هل سيقوم البرنامج بتخصيص مثل هذه المصفوفات الكبيرة؟ هل سيقضي برنامجك بالفعل قدرًا كبيرًا من وقته في تخصيص الذاكرة ، أم أنه يقضي معظم وقته الوصول الذاكرة والقيام بالحسابات؟ يستغرق وقتا طويلا في الواقع فعل أي شيء يحتوي على مجموعة 4 جيجابايت ، مقارنة بالوقت الذي استغرقته لتخصيصه ، وهذا يعني أنك يجب أن تكون قلقًا بشأن أداء "أي شيء" ، وليس أداء التخصيص. يهتم العداءون كثيرًا بالسرعة التي ينزلون بها. الماراثون المتسابقين ، وليس كثيرا.
عليك أيضًا أن تكون حذراً لكيفية القياس. يجب أن تقارن على سبيل المثال malloc(size)
ضد new char[size]
. إذا قمت باختبار malloc(size)
ضد new char[size]()
ثم إنها مقارنة غير عادلة لأن الأخير يحدد الذاكرة إلى 0 والآخر لا. قارن ضد calloc
بدلاً من ذلك ، ولكن لاحظ ذلك أيضًا malloc
و calloc
كلاهما متاح من C ++ في الحدث (غير المحتمل) الذي يثبتانه بشكل أسرع بشكل ملموس.
في النهاية ، على الرغم من ذلك ، إذا كان المؤلف "يمتلك" أو بدأ المشروع ، ويفضل الكتابة في C بدلاً من C ++ ، فعليه ألا يبرر هذا القرار بمطالبات الأداء على الأرجح ، فيجب عليه تبريره بالقول "أنا أفضل C ، وهذا ما أستخدمه ". عادةً عندما يقدم شخص ما مطالبة مثل هذا حول أداء اللغة ، واتضح أن الاختبار غير صحيح ، فإنك تكتشف أن الأداء ليس هو السبب الحقيقي لتفضيل اللغة. إن إثبات أن المطالبة الخاطئة لن يتسبب بالفعل في بدء مؤلف هذا المشروع فجأة من الإعجاب بـ C ++.
لا يوجد فرق حقيقي بين C و C ++ من حيث تخصيص الذاكرة. يحتوي C ++ على المزيد من البيانات "المخفية" ، مثل المؤشرات الافتراضية وما إلى ذلك ، إذا اخترت أن يكون لديك طرق افتراضية على كائناتك. لكن تخصيص مجموعة من chars باهظة الثمن في C كما في C ++ ، في الواقع ، ربما يستخدمان Malloc للقيام بذلك. من حيث الأداء ، يستدعي C ++ مُنشئًا لكل كائن في الصفيف. لاحظ أن هذا يتم فقط إذا كان هناك واحد ، فإن المُنشئ الافتراضي لا يفعل شيئًا ويتم تحسينه بعيدًا.
طالما أنك تجمع بين البيانات المتوفرة ، لتجنب تجزئة الذاكرة ، يجب أن تكون على ما يرام. إذا كان لديك هيكل POD بسيط بدون طرق افتراضية ، وبدون مُنشئين ، فلا يوجد فرق.
الشيء الوحيد في إهمال C ++ هو تعقيد إضافي - ادمج ذلك مع مبرمج يستخدمه بشكل غير صحيح ، ويمكنك بسهولة إبطاء بشكل ملحوظ. سيمنحك استخدام برنامج التحويل البرمجي C ++ بدون ميزات C ++ نفس الأداء. باستخدام C ++ بشكل صحيح ، لديك بعض المقابلات لتكون أسرع.
اللغة ليست مشكلتك, ، تخصيص المصفوفات الكبيرة واضطرابها.
الخطأ الرئيسي المميت الذي يمكن أن ترتكبه في التخصيص (في أي من اللغة) هو تخصيص 16 جم من الذاكرة ، وتهيئتها إلى الصفر ، فقط لملء القيم الفعلية لاحقًا.
اكتساب معظم الأداء الذي أتوقعه من تحسينات الخوارزمية التي تعمل على تحسين موقع المرجع.
اعتمادًا على نظام التشغيل الأساسي ، قد تؤثر أيضًا على خوارزميات التخزين المؤقت - على سبيل المثال ، مما يشير إلى أن مجموعة من المذكرات تتم معالجتها بالتتابع فقط.
لتخصيص البيانات الأولية ، لا ينبغي أن يكون هناك فرق بين C و C ++ على معظم الأنظمة لأنها عادة ما تستخدم آليات مكتبة وقت التشغيل نفسها. أتساءل عما إذا كان هذا هو المأخير القياسي الكلاسيكي حيث قاموا أيضًا بقياس وقت تشغيل مكالمات المُنشئ في C ++ ونسيهم بشكل مناسب تضمين وقت تشغيل أي نوع من رمز التهيئة في C.
وأيضًا ، لا تحتوي الوسيطة "أكثر موثوقية (بشأن تسرب الذاكرة)" على أي ماء إذا كنت تستخدم RAII في C ++ (كما يجب). ما لم يشير شخص ما إلى جعله يتسرب بشكل أكثر موثوقية ، فإن استخدام المؤشرات الذكية ، ودروس الحاويات ، سيقلل من احتمال حدوث تسرب ، وليس زيادة.
إن اهتماماتي الرئيسية بتخصيص الكثير من الذاكرة ستكون ذات شقين:
- إذا كنت تقترب من حد الذاكرة الفعلية على الآلات التي تقوم بتشغيلها في محاكاة مونت كارلو ، فهذه طريقة جيدة لتقليل الأداء لأن القرص قد يبدأ في السحق عندما يحتاج نظام الذاكرة الافتراضية إلى بدء تشغيل الترحيل كثيرًا . الذاكرة الافتراضية ليست "مجانية" على الرغم من أن الكثير من الناس يعتقدون أنها كذلك.
- يجب مراعاة تخطيط البيانات بعناية لزيادة استخدام ذاكرة التخزين المؤقت للمعالج إلى الحد الأقصى ، وإلا ستفقد جزئيًا فوائد الحفاظ على البيانات في الذاكرة الرئيسية في المقام الأول.
إذا كان تخصيص الذاكرة عنق الزجاجة في مثل هذا الرمز ، أقترح إعادة تصميمه ، وليس تغيير اللغة لتخصيص أسرع. إذا قمت بتخصيص الذاكرة مرة واحدة ثم تنفيذ الكثير من الحسابات ، أتوقع أن تكون تلك الحسابات عنق الزجاجة. إذا كانت تكلفة التخصيص مهمة ، فكل شيء خاطئ هنا.
يمكنك استخدام عائلة C لوظائف تخصيص الذاكرة في C ++ أيضًا: كلا المعيار malloc
و free
, realloc
لتكبير المصفوفات/القبيلة و alloca
لتخصيص الذاكرة على المكدس.
إذا ذهبت مع new
, ، سوف يخصص ذاكرة أكثر مما هو مطلوب (في الغالب أثناء تصحيح الأخطاء) وإجراء فحوصات إضافية للتناسق. كما سيتصل بمنشئ الفصول. في إصدار (-O3
) بناء الفرق سيكون ضئيلًا لمعظم التطبيقات.
ماذا الآن new
يجلب أن malloc لا هو في المكان new
. يمكنك مضايقة المخزن المؤقت ثم استخدام الموقع new
لوضع هيكلك في هذا المخزن المؤقت ، مما يجعل "تخصيص" فوريًا.
الكل في الكل ، لن أبتعد عن C بسبب مخاوف الأداء. إذا كان هناك أي شيء ، فسيكون الكود الخاص بك أكثر كفاءة لأن الفئات تمر this
مؤشر في السجلات بدلا من المعلمات مثل في المكافئ C. سبب حقيقي للابتعاد عن C هو حجم وقت تشغيل C ++. إذا قمت بتطوير برامج للأنظمة المدمجة أو البرامج المحملة بالتمهيد ، فلن تتمكن من تضمين وقت تشغيل ~ 4 ميغابايت. بالنسبة للتطبيقات العادية ، لن يحدث هذا فرقًا.
إذا كنت بحاجة إلى تخزين 4-16 جيجابايت من صفائف البيانات في الذاكرة أثناء الحساب ولديه جهازك فقط 2 جيجابايت من الذاكرة الفعلية ، فماذا؟
ماذا لو كان لجهازك 16 جيجابايت من الذاكرة الفعلية؟ هل لا يتناول نظام التشغيل أي ذاكرة فعلية؟
هل يتيح لك نظام التشغيل حتى مساحة عنوان 4 جيجابايت ، 16 جيجابايت ، إلخ؟
أقترح أنه إذا كان الأداء قيد التنفيذ الأساسي ، فعندئذ فهم كيفية استخدام المنصات ، والتي تهدف إلى استخدامها ، والوظيفة والأداء أكثر أهمية بكثير من مسألة أي اختلاف في الأداء القابل للقياس بين C و C ++ مع إعطاء بيئات متطابقة وخوارزميات.