c++ - لماذا لا يقوم المترجم بتحسين النقطة العائمة*2 إلى زيادة exponent؟




performance optimization (6)

لقد لاحظت في كثير من الأحيان تحويل مجلس التعاون الخليجي مضاعفات في التحولات في الملف التنفيذي. قد يحدث شيء مشابه عند ضرب int و a float . على سبيل المثال ، قد يؤدي 2 * f ببساطة إلى زيادة الأس f بمقدار 1 ، مما يوفر بعض الدورات. هل يقوم -ffast-math ، ربما إذا طلب منهم أحدهم القيام بذلك (على سبيل المثال ، عبر -ffast-math بشكل عام) ، أن يفعلوا ذلك؟

هل المترجمون أذكياء بشكل عام بما يكفي للقيام بذلك ، أو هل أحتاج إلى القيام بذلك بنفسي باستخدام الدالة scalb*() أو ldexp()/frexp() ؟


على سبيل المثال ، قد يؤدي 2 * f ببساطة إلى زيادة الأس f بمقدار 1 ، مما يوفر بعض الدورات.

هذا ببساطة غير صحيح.

أولاً ، لديك العديد من حالات الزوايا مثل الصفر ، واللانهاية ، ونان ، والدنمان. ثم لديك مشكلة الأداء.

سوء الفهم هو أن زيادة الأس ليست أسرع من القيام بعملية الضرب.

إذا نظرت إلى تعليمات الأجهزة ، لا توجد طريقة مباشرة لزيادة الأس. إذن ما عليك القيام به هو:

  1. Bitwise تحويل إلى عدد صحيح.
  2. زيادة الأس.
  3. Bitwise تحويل إلى نقطة عائمة.

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

لذلك السبب في أن المترجم لا يقوم بهذا "التحسين" لأنه ليس أسرع.


إذا كنت تعتقد أن الضرب في اثنين يعني زيادة الأس بنسبة 1 ، فكر مرة أخرى. فيما يلي الحالات المحتملة للحساب العائم IEEE 754:

الحالة 1: تبقى اللانهاية و NaN بدون تغيير.

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

الحالة 3: أرقام الفاصلة العائمة المعيارية مع الأس أقل من الأس الأقصى الممكن زيادة الأس الخاصة بها بمقدار واحد. ييب !!!

الحالة 4: الأرقام ذات الفاصلة العائمة المعطلة ذات أعلى مجموعة بتة عشريّة قد ازدادت أسها الواحد ، مما حولها إلى أرقام عادية.

الحالة رقم 5: الأرقام العائمة المعطلة ذات أعلى قيمة ، بما في ذلك +0 و -0 ، انتقلت عشريتها إلى اليسار بمركز واحد ، تاركة الأس من دون تغيير.

إنني أشك كثيراً في أن المترجم الذي ينتج رمزاً صحيحاً يتعامل مع كل هذه الحالات بشكل صحيح سيكون في أي مكان بسرعة مثل النقطة العائمة المضمنة في المعالج. وهي مناسبة فقط للتكاثر بنسبة 2.0. لضرب 4.0 أو 0.5 ، تنطبق مجموعة جديدة كاملة من القواعد. وبالنسبة لحالة الضرب في 2.0 ، قد تحاول استبدال x * 2.0 بـ x + x ، ويقوم الكثير من المترجمين بذلك. هذا هو أنهم يفعلون ذلك ، لأن المعالج قد يكون قادراً على سبيل المثال على القيام بإضافة واحدة وضرب واحد في نفس الوقت ، ولكن ليس واحدا من كل نوع. لذلك في بعض الأحيان تفضل x * 2.0 ، وأحيانًا x + x ، اعتمادًا على العمليات الأخرى التي تحتاج إلى القيام بها في نفس الوقت.


في الواقع ، هذا ما يحدث في الأجهزة.

يتم تمرير 2 أيضًا إلى FPU كرقم نقطة عائمة ، مع جزء من 1.0 وأسط من 2 ^ 1. للضرب ، تضاف الأسس ، وتضاعف المذياعات.

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


قد يكون من المفيد لمجمّعي الأنظمة المضمّنة الحصول على مقياس زائف خاص بمقياس الطاقة لكلٍّ من pseudo-op والذي يمكن ترجمته بواسطة مُولِّد الشفرات بأي طريقة كانت مثالية للجهاز المعني ، بما أن هناك بعض المعالجات المضمنة التي تركز على قد يكون الأس ترتيب من حيث الحجم أسرع من القيام بكامل القوة من اثنين من الضرب ، ولكن على micros مضمن حيث الضرب هو أبطأ ، يمكن أن يحقق المترجم على الأرجح دفعة أكبر أداء من خلال وجود روتين الفاصلة العائمة مضاعفة تحقق حججها في وقت التشغيل بحيث تخطي أجزاء من سلسلة غير الصفر.


لا يتعلق الأمر بمجمعي الكتب أو المجمعين الذين ليسوا أذكياء. إنها تشبه أكثر الامتثال للمعايير وإنتاج كل "الآثار الجانبية" الضرورية ، مثل: Infs و Nans و denormals.

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






compiler-optimization