يمكن أن دول مجلس التعاون الخليجي/g++ أخبرني عندما يتجاهل بلدي التسجيل ؟

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

سؤال

عند التحويل البرمجي C/C++ باستخدام رموز دول مجلس التعاون الخليجي/g++, إذا كان يتجاهل بلدي التسجيل ، يمكن أن تقول لي ؟ على سبيل المثال, في هذه المدونة

int main()
{
    register int j;
    int k;
    for(k = 0; k < 1000; k++)
        for(j = 0; j < 32000; j++)
            ;
    return 0;
}

ي سيتم استخدام السجل ، ولكن في هذه المدونة

int main()
{
    register int j;
    int k;
    for(k = 0; k < 1000; k++)
        for(j = 0; j < 32000; j++)
            ;
    int * a = &j;
    return 0;
}

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

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

المحلول

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

فيما يلي موضوع إعلامي حول هذا الموضوع: http://gcc.gnu.org/ml/gcc/2010-05/msg00098.html . العودة في الأيام الخوالي ، register في الواقع ، ساعد المترجمون على تخصيص متغير في السجلات ، ولكن يمكن تحقيق اليوم تخصيص التسجيل على النحو الأمثل ، تلقائيًا ، دون تلميحات. تستمر الكلمة الرئيسية في تقديم غرضين في ج:

  1. في C ، يمنعك من أخذ عنوان المتغير. نظرًا لأن السجلات لا تحتوي على عناوين ، فإن هذا التقييد يمكن أن يساعد برنامج التحويل البرمجي C بسيط. (لم تكن موجودة مجمعات C ++ بسيطة.)
  2. أ register لا يمكن إعلان الكائن restrict. لان restrict يتعلق بالعناوين ، تقاطعها لا معنى له. (C ++ ليس لديه بعد restrict, ، وعلى أي حال ، هذه القاعدة تافهة بعض الشيء.)

بالنسبة لـ C ++ ، تم إهمال الكلمة الرئيسية منذ C ++ 11 و مقترح للإزالة من المراجعة القياسية المقرر لعام 2017.

استخدم بعض المترجمين register على إعلانات المعلمة لتحديد اتفاقية استدعاء الوظائف ، مع ABI يسمح ABI المعلمات المختلطة القائمة على السجل. يبدو أن هذا غير متطابق ، يميل إلى الحدوث مع بناء جملة ممتدة register("A1"), ، وأنا لا أعرف ما إذا كان أي مترجم من هذا القبيل لا يزال قيد الاستخدام.

نصائح أخرى

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

لا يمكنك الحصول على عنوان سجل في C ، بالإضافة إلى أن المترجم يمكن أن يتجاهلك تمامًا ؛ C99 Standard ، القسم 6.7.1 (بي دي إف):

يجوز للتنفيذ التعامل مع أي إعلان سجل ببساطة كإعلان تلقائي. ومع ذلك ، سواء تم استخدام التخزين القابل للمعالجة أم لا ، فلا يمكن حساب عنوان أي جزء من كائن معلنه سجل محدد من فئة التخزين ، إما بشكل صريح (عن طريق استخدام Unary والمشغل كما تمت مناقشته في 6.5.3.2) أو ضمنيًا ( عن طريق تحويل اسم صفيف إلى مؤشر كما تمت مناقشته في 6.3.2.1). وبالتالي ، فإن المشغل الوحيد الذي يمكن تطبيقه على صفيف يُعلن عنه سجل محدد من فئة التخزين هو SizeOF.

ما لم تكن تتجول على AVRs أو صور 8 بت ، فمن المحتمل أن يضحك المترجم عليك معتقدًا أنك تعرف أفضل وتجاهل نداءاتك. حتى عليهم ، لقد اعتقدت أنني أعرف بشكل أفضل عدة مرات ووجدت طرقًا لخداع المترجم (مع بعض ASM المضمّن) ، لكن الكود الخاص بي قد انفجر لأنه كان عليه تدليك مجموعة من البيانات الأخرى للعمل حول عنادتي.

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

ملاحظة:قبل السكان المحليين هنا أعني:المتغيرات العددية من فئة تخزين السيارات (أو "تسجيل") ، والتي لم تستخدم المعامل '&'.المجمعين في بعض الأحيان يمكن أن تنفصل السيارات البنيات والنقابات أو صفائف إلى الفردية 'المحلية' المتغيرات أيضا.

ولتوضيح ذلك:لنفترض أنني أكتب هذا في الجزء العلوي من وظيفة:

int factor = 8;

..وبعد ذلك فقط استخدام factor المتغير هو ضرب من قبل مختلف الأمور:

arr[i + factor*j] = arr[i - factor*k];

في هذه الحالة - تحاول ذلك إذا كنت تريد - لن يكون هناك factor متغير.رمز التحليل تبين أن factor دائما 8 ، وهكذا كل التحولات سوف تتحول إلى <<3.إذا كنت فعلت نفس الشيء في عام 1985 ، ج ، factor سوف تحصل على موقع على المكدس ، وسيكون هناك multipilies منذ المجمعين في الأساس عملت كشف في وقت لم تذكر أي شيء عن قيم المتغيرات.ثم العودة المبرمجين سيكون أكثر احتمالا لاستخدام #define factor 8 للحصول على أفضل مدونة في هذا الوضع ، مع الحفاظ على قابل للتعديل factor.

إذا كنت تستخدم -O0 (التحسين إيقاف) - كنت في الواقع الحصول على متغير factor.هذا سوف يسمح لك ، على سبيل المثال ، إلى الخطوة على factor=8 البيان ثم تغيير factor إلى 11 مع المصحح و الاستمرار.من أجل هذا العمل ، فإن المترجم لا يمكن أن تبقى أي شيء في السجلات بين البيانات ، باستثناء المتغيرات التي تم تعيينها إلى سجلات محددة;و في هذه الحالة المصحح هو أبلغ من هذا.وأنه لا يمكن أن أحاول أن أعرف أي شيء عن قيم المتغيرات ، لأن المصحح يمكن تغييرها.وبعبارة أخرى, تحتاج 1985 الحالة إذا كنت ترغب في تغيير المتغيرات المحلية في حين التصحيح.

الحديث المجمعين عموما تجميع وظيفة على النحو التالي:

(1) عندما متغير محلي يتم تعيين أكثر من مرة في وظيفة المحول البرمجي بإنشاء مختلفة "الإصدارات" المتغير بحيث يكون لكل واحدة فقط من تعيينه في مكان واحد.كل من يقرأ' المتغير الرجوع إلى إصدار محدد.

(2) كل من هذه المحليين يتم تعيين 'الظاهري' التسجيل.المتوسطة حساب النتائج أيضا تعيين المتغيرات/سجلات;لذلك

  a = b*c + 2*k;

يصبح شيئا مثل

       t1 = b*c;
       t2 = 2;
       t3 = k*t2;
       a = t1 + t3;

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

(4) المترجم ثم يحاول خريطة كل هذه الافتراضية يسجل في السجلات الفعلية من أجل توليد التعليمات البرمجية.لأن كل الافتراضية سجل له عمر محدود من الممكن إعادة استخدامها الفعلي السجلات بشكل كبير 't1' في أعلاه هو فقط في حاجة إلى إضافة يولد 'a', حتى أنها يمكن أن تعقد في نفس التسجيل 'a'.عندما لا يوجد ما يكفي من تسجيل بعض الظاهرية السجلات يمكن تخصيص الذاكرة-أو-قيمة يمكن أن تعقد في بعض السجل المخزنة في الذاكرة لفترة من الوقت ، تحميلها مرة أخرى إلى (ربما) مختلفة تسجيل في وقت لاحق.على تحميل متجر آلة, حيث القيم فقط في سجلات يمكن استخدامها في العمليات الحسابية ، هذه الاستراتيجية الثانية يلائم ذلك بشكل جيد.

من فوق هذا يجب أن يكون واضحا:فإنه من السهل لتحديد الظاهرية التسجيل تعيين إلى factor هو نفس المستمر '8' و لذلك كل الضرب من قبل factor هي الضرب 8.حتى إذا factor هو تعديل في وقت لاحق من هذا "الجديد" المتغير لا يؤثر على قبل الاستخدامات factor.

آثار أخرى ، إذا كنت أكتب

 vara = varb;

..قد أو قد لا يكون الحال أن هناك المقابلة نسخ في المدونة.على سبيل المثال

int *resultp= ...
int acc = arr[0] + arr[1];
int acc0 = acc;    // save this for later
int more = func(resultp,3)+ func(resultp,-3);
acc += more;         // add some more stuff
if( ...){
    resultp = getptr();
    resultp[0] = acc0;
    resultp[1] = acc;
}

في أعلاه اثنين من "الإصدارات" من لجنة التنسيق الإدارية (الأولي ، بعد إضافة 'أكثر') يمكن أن يكون في اثنين من مختلف السجلات و 'acc0' ثم يكون نفس inital 'acc'.لذلك لا تسجيل نسخة من شأنها أن تكون هناك حاجة 'acc0 =acc'.نقطة أخرى:إن 'resultp' يتم تعيين مرتين منذ التعيين الثاني يتجاهل القيمة السابقة ، هناك أساسا اثنين متميزة 'resultp' المتغيرات في القانون ، وهذا هو تحديدها بسهولة عن طريق التحليل.

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

إذا كنت ترغب في معرفة المزيد يمكنك أن تبدأ من هنا: http://en.wikipedia.org/wiki/Static_single_assignment_form

الهدف من هذا الجواب هو (أ) تعطي فكرة عن كيفية الحديث المجمعين العمل و (ب) نشير إلى أن يسأل المترجم ، إذا كان من ذلك النوع ، لوضع معين متغير محلي في سجل -- لا معنى له.كل متغير' يمكن أن ينظر إليها من قبل محسن العديد من المتغيرات التي قد تكون تستخدم بشكل كبير في الحلقات ، والبعض الآخر لا.بعض المتغيرات سوف تتلاشى -- على سبيل المثالمن خلال كونها ثابتة ؛ أو أحيانا متغير temp استخدامها في مجموعة مبادلة.أو الحسابات غير المستخدمة فعلا.المجمع مجهز لاستخدام نفس التسجيل في أشياء مختلفة في أجزاء مختلفة من القانون ، حسب ما هو في الواقع أفضل على الجهاز الذي يتم تجميع.

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

فيما يتعلق بتقييد أنه لا يمكنك أن تأخذ عنوانه سجل متغير:ومن الواضح أن هناك أي وسيلة لتنفيذ أخذ عنوان المتغير الذي عقد في السجل ، ولكن قد تسأل - منذ المترجم له السلطة التقديرية في تجاهل 'التسجيل' لماذا هذه القاعدة في المكان ؟ لماذا لا يستطيع المترجم تجاهل مجرد "تسجيل" إذا حدث العنوان ؟ (كما هو الحال في C++).

مرة أخرى, عليك أن تذهب مرة أخرى إلى القديم المترجم.الأصلي K+R أن المترجم تحليل متغير محلي الإعلان ، ثم على الفور تقرر ما إذا كنت تريد تعيين إلى تسجيل أو لا (و إذا كان الأمر كذلك ، الذي سجل).ثم الانتقال إلى ترجمة التعبيرات التي ينبعث منها المجمع عن كل بيان في وقت واحد.إذا كان في وقت لاحق وجدت أن كنت أخذ عنوان 'التسجيل' المتغير الذي تم تعيينه إلى التسجيل ، لم يكن هناك أي طريقة للتعامل مع ذلك ، لأن المهمة كانت في العام الذي لا رجعة فيه من قبل ثم.كان من الممكن ، ومع ذلك ، من أجل توليد رسالة خطأ و التوقف عن تجميع.

خلاصة القول يبدو أن 'التسجيل' هو أساسا عفا عليها الزمن:

  • C++ compilers تجاهله تماما
  • ج المجمعين تجاهل ذلك إلا لفرض قيود حول & - وربما لا تجاهله في -O0 حيث يمكن أن يؤدي في الواقع إلى تخصيص النحو المطلوب.في O0 كنت لا تشعر بالقلق إزاء رمز السرعة على الرغم من.

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


الإضافة

أنا استبعاد أعلاه, من المقرر تعريف السكان المحليين', المتغيرات التي & يتم تطبيق (هذه هي بالطبع المدرجة في المعتاد للكلمة من معنى).

النظر في التعليمات البرمجية أدناه:

void
somefunc()
{
    int h,w;
    int i,j;
    extern int pitch;

    get_hw( &h,&w );  // get shape of array

    for( int i = 0; i < h; i++ ){
        for( int j = 0; j < w; j++ ){
            Arr[i*pitch + j] = generate_func(i,j);
        }
    }
}

هذا قد تبدو غير مؤذية تماما.ولكن إذا كنت تشعر بالقلق إزاء سرعة التنفيذ ، النظر في هذا:المترجم هو تمرير عناوين h و w إلى get_hw, ثم في وقت لاحق الدعوة generate_func.دعونا نفترض أن المترجم لا يعرف شيئا عن ما في تلك الوظائف (التي هي الحالة العامة).مترجم يجب أن نفترض أن الدعوة إلى generate_func يمكن تغيير h أو w.هذا هو قانوني تماما استخدام المؤشر مرت get_hw - هل يمكن تخزينه في مكان ما ومن ثم استخدامها في وقت لاحق ، طالما نطاق تحتوي على h,w لا يزال في اللعب, قراءة أو كتابة تلك المتغيرات.

وبالتالي يجب المترجم متجر h و w في الذاكرة على المكدس ، و لا يمكن تحديد أي شيء في وقت مبكر عن كم حلقة سيتم تشغيل.حتى بعض التحسينات سوف يكون من المستحيل ، وحلقة يمكن أن يكون أقل كفاءة نتيجة (في هذا المثال ، هناك استدعاء دالة في الحلقة الداخلية على أي حال, لذلك قد لا تجعل الكثير من الفرق, ولكن النظر في الحالة التي يكون فيها هناك والتي هي وظيفة أحيانا ودعا في الحلقة الداخلية ، اعتمادا على بعض الشروط).

آخر المسألة هنا هي أن generate_func يمكن تغيير pitch, وهكذا i*pitch يحتاج إلى القيام به في كل وقت وليس فقط عند i التغييرات.

يمكن أن يكون مشفر مثل:

void
somefunc()
{
    int h0,w0;
    int h,w;
    int i,j;
    extern int pitch;
    int apit = pitch;

    get_hw( &h0,&w0 );  // get shape of array
    h= h0;
    w= w0;

    for( int i = 0; i < h; i++ ){
        for( int j = 0; j < w; j++ ){
            Arr[i*apit + j] = generate_func(i,j);
        }
    }
}

الآن المتغيرات apit,h,w كلها 'آمنة' المحليين بمعنى أنا المحددة أعلاه ، البرمجي يمكن أن تكون متأكدا من أنها لن تغير أي وظيفة المكالمات.على افتراض أنا لا تعديل أي شيء في generate_func, كود سوف يكون له نفس التأثير كما كان من قبل ولكن يمكن أن تكون أكثر كفاءة.

ينس Gustedt اقترح استخدام "تسجيل" الكلمة كوسيلة من علامات المتغيرات الرئيسية لمنع استخدام & منهم على سبيل المثالمن قبل الآخرين الحفاظ على رمز (لن تؤثر على التعليمات البرمجية التي تم إنشاؤها منذ المترجم يمكن أن تحدد عدم & من دون ذلك).من جهتي, أعتقد دائما بعناية قبل تطبيق & إلى أي محلي العددية في الوقت الحاسم المجال من القانون ، و في رأيي باستخدام 'التسجيل' لتطبيق هذا هو خفي قليلا, ولكن أستطيع أن أرى نقطة (لسوء الحظ أنها لا تعمل في C++ منذ المترجم تجاهل مجرد "تسجيل").

بالمناسبة, من حيث كفاءة رمز, أفضل طريقة للحصول على وظيفة عودة اثنين من القيم مع البنية:

struct hw {  // this is what get_hw returns
   int h,w;
};

void
somefunc()
{
    int h,w;
    int i,j;

    struct hw hwval = get_hw();  // get shape of array
    h = hwval.h;
    w = hwval.w;
    ...

هذا قد تبدو مرهقة (و مرهقة الكتابة) ، ولكن فإنه سيتم إنشاء رمز أنظف من الأمثلة السابقة.إن 'البنية hw' سوف يكون في الواقع عاد في اثنين من السجلات (على الأكثر حداثة أبيس على أي حال).ويرجع ذلك إلى الطريقة 'hwval' البنية ، محسن فعال تقسيمها إلى اثنين 'المحليين' hwval.h و hwval.w, ثم يقرر أن هذه هي يعادل h و w - حتى hwval سوف أساسا تختفي في المدونة.لا مؤشرات تحتاج إلى تمرير من لا وظيفة تعديل وظيفة أخرى هي المتغيرات عن طريق المؤشر.انها مجرد مثل وجود اثنين متميزة العددية عودة القيم.هذا هو أسهل بكثير من القيام به الآن في C++11 - مع std::tie و std::tuple, يمكنك استخدام هذا الأسلوب مع أقل الإسهاب (و دون الحاجة إلى كتابة البنية التعريف).

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

يبدو أن C ++ يتجاهل register, ، حسنًا ، يجب أن يكون لديهم سبب لذلك ، لكنني أجد أنه من المحزن مرة أخرى العثور على أحد هذه الاختلافات الدقيقة عندما يكون رمز صالح لأحد غير صالح للآخر.

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