c - لماذا لا يستطيع(أو لا) أن يقوم المجمع بتحسين حلقة الإضافة المتوقعة في عملية الضرب؟




performance compiler-optimization (4)

هذا سؤال يتبادر إلى الذهن أثناء قراءة الإجابة اللامعة من جانب Mysticial على السؤال: لماذا يكون من الأسرع معالجة مصفوفة Mysticial من صفيف غير مجزأ ؟

السياق للأنواع المعنية:

const unsigned arraySize = 32768;
int data[arraySize];
long long sum = 0;

يشرح في إجابته أن برنامج Intel Compiler (ICC) يعمل على تحسين ذلك:

for (int i = 0; i < 100000; ++i)
    for (int c = 0; c < arraySize; ++c)
        if (data[c] >= 128)
            sum += data[c];

... إلى شيء يعادل هذا:

for (int c = 0; c < arraySize; ++c)
    if (data[c] >= 128)
        for (int i = 0; i < 100000; ++i)
            sum += data[c];

يتم التعرف على محسّن أن هذه مكافئة وبالتالي يتم تبادل حلقات ، نقل الفرع خارج الحلقة الداخلية. ذكي جدا!

لكن لماذا لا يفعل هذا؟

for (int c = 0; c < arraySize; ++c)
    if (data[c] >= 128)
        sum += 100000 * data[c];

نأمل أن يقدم (Mysticial) (أو أي شخص آخر) إجابة رائعة بنفس القدر. لم أتعلم أبداً عن التحسينات التي تمت مناقشتها في هذا السؤال الآخر من قبل ، لذلك أنا ممتن حقًا لهذا الأمر.


حسنًا ، أعتقد أن بعض المترجمين قد يقومون بهذا النوع من التحسين ، على افتراض أننا نتحدث عن علم حساب صحيح.

في الوقت نفسه ، قد يرفض بعض المترجمين القيام بذلك لأن استبدال الإضافات المتكررة مع الضرب قد يؤدي إلى تغيير سلوك تجاوز الكود. بالنسبة للأنواع المتكاملة unsigned لا ينبغي أن تحدث فرقا ، لأن سلوكها الفائض محدد بالكامل من قبل اللغة. ولكن بالنسبة للأشهر الموقّعة فقد (ربما لا يكون على منصة 2 تكمل رغم ذلك). صحيح أن الفائض الموقّع يؤدي في الواقع إلى سلوك غير معروف في C ، وهذا يعني أنه ينبغي أن يكون من المناسب تمامًا تجاهل دلالات الفائض هذه تمامًا ، وليس كل الشائعات المجمعة شجاعات كافية للقيام بذلك. وغالبًا ما يوجه الكثير من الانتقادات من الحشد "C هو مجرد لغة التجميع عالية المستوى". (تذكر ما حدث عندما عرض مجلس التعاون الخليجي تحسينات استنادًا إلى دلالات التعرج الصارمة؟)

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


لا تنطبق هذه الإجابة على الحالة المحددة المرتبطة ، ولكنها تنطبق على عنوان السؤال ، وقد تكون مثيرة للاهتمام لقراء المستقبل:

نظرًا للدقة المحدودة ، فإن تكرار نقطة العائمة المتكررة لا يعادل الضرب . يعتبر:

float const step = 1e-15;
float const init = 1;
long int const count = 1000000000;

float result1 = init;
for( int i = 0; i < count; ++i ) result1 += step;

float result2 = init;
result2 += step * count;

cout << (result1 - result2);

عرض توضيحي: http://ideone.com/7RhfP


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


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

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





compiler-optimization