java - إعلان كائن داخل أو خارج حلقة؟




performance (11)

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

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

هل هناك أي عقوبة أداء لمقتطف الشفرة التالي؟

for (int i=0; i<someValue; i++)
{
    Object o = someList.get(i);
    o.doSomething;
}

أو هل هذا الرمز في الواقع أكثر منطقية؟

Object o;
for (int i=0; i<someValue; i++)
{
    o = someList.get(i);
    o.doSomething;
}

إذا كان في رمز البايت هذين هما معادلات تماما ومن الواضح أن الطريقة الأولى تبدو أفضل من حيث النمط، ولكن أريد أن تأكد من هذا هو الحال.


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


لا تحسن قبل الأوان. أفضل من أي من هذه هي:

for(Object o : someList) {
    o.doSomething();
}

لأنه يلغي نمطي ويوضح القصد.

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



هذه لها دلالات مختلفة. ما هو أكثر وضوحا؟

إعادة استخدام كائن ل "أسباب الأداء" غالبا ما تكون خاطئة.

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

ما هي هذه الأسباب؟ كيف نموذج الكائن الخاص بك وتعكس تلك الأسباب؟


يجب أن أعترف أنني لا أعرف جافا. ولكن هل هذين المعادلين؟ هل عمر الكائن هو نفسه؟ في المثال الأول، أفترض (لا أعرف جافا) أن o سوف تكون مؤهلة لجمع القمامة فورا ينتهي حلقة.

ولكن في المثال الثاني بالتأكيد سوف لن تكون مؤهلة لجمع القمامة حتى يتم الخروج من النطاق الخارجي (لا يظهر)؟


الرمز الأول هو أفضل لأنه يقيد نطاق o متغير إلى كتلة. من منظور الأداء، قد لا يكون له أي آثار في جافا، ولكن قد يكون في المجمعين على مستوى أقل. قد يضعون المتغير في سجل إذا كنت تفعل الأول.

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

بناء كائن باستخدام new يختلف تماما عن مجرد إعلان ذلك، بطبيعة الحال.

أعتقد أن القراءة أكثر أهمية أن الأداء ومن وجهة نظر القراءة، أول رمز هو بالتأكيد أفضل.


كشخص يحافظ على رمز أكثر من يكتب التعليمات البرمجية.

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

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


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

أولا، ضع في اعتبارك أن المتغير المحلي "o" في المثال هو مؤشر، وليس كائن فعلي.

يتم توزيع كافة المتغيرات المحلية على حزمة التشغيل في فتحات 4 بايتات. الزوجي والطول تتطلب اثنين من فتحات؛ والأولويات الأخرى والمؤشرات تأخذ واحدة. (حتى بولنز تأخذ فتحة كاملة)

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

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

في كلتا الحالتين، سيتم إنشاء نفس البايتود، تحديث نفس الفتحة في كومة وقت التشغيل.

وبعبارة أخرى، لا عقوبة الأداء على الإطلاق.

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

for (...) { Object o = ... }
for (...) { Object o = ... }

مع

Object o;
for (...) {  /* loop 1 */ }
for (...) { Object x =...; }

في المثال الأول، تتطلب كلا الحلقات تخصيص نفس كومة وقت التشغيل.

في المثال الثاني، لأن "س" يعيش الماضي حلقة، "x" يتطلب فتحة إضافية كومة وقت التشغيل.

نأمل أن يساعد هذا، - سكوت


نقلا عن نوث، الذي قد يكون نقلا عن هوير :

تحسين سابق لأوانه هو جذر كل الشر.

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

قارن هذا مع عدد الأخطاء التي من المحتمل أن تمنعها عن طريق تحديد نطاق المتغير بشكل صحيح باستخدام إعلان في حلقة ...


عند استخدام مؤشرات الترابط متعددة (إذا كان لديك 50+) ثم وجدت هذا أن تكون وسيلة فعالة جدا للتعامل مع مشاكل موضوع شبح:

Object one;
Object two;
Object three;
Object four;
Object five;
try{
for (int i=0; i<someValue; i++)
{
o = someList.get(i);
o.doSomething;
}
}catch(e){
e.printstacktrace
}
finally{
one = null;
two = null;
three = null;
four = null;
five = null;
System.gc();
}






performance