سؤال

إذا حصلت على C99 restrict اليمين الكلمة الأساسية، المؤهلة للمؤشر معها هو وعد جعل البيانات لن يتم تعديل مراجع تكنولوجيا المعلومات خلف ظهر التحويل البرمجي من خلال التعرج.

على النقيض من ذلك، الطريقة التي أفهمها const Catherifier هو كوثائق مطبقة من مترجم لن يتم تعديل كائن معين خلف الجزء الخلفي من رمز الكتابة البشري. قد يحصل المترجم على تلميح كأثر جانبي، ولكن كمبرمج لا أهتم حقا.

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

أيضا، هل هناك شيء لفهمه في حقيقة ذلك restrict يؤهل مؤشر بدلا من البيانات التي يشير إليها (كما const هل) ؟

تحرير: اعتقدت في الأصل ذلك restrict يمكن أن يكون لها آثار مع رمز الخيوط، ولكن هذا يبدو خطأ لذلك أزيل المراجع إلى خيوط من السؤال لتجنب مربك القراء.

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

المحلول

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

نصائح أخرى

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

const و restrict هي مفاهيم مختلفة، وهذا ليس هو الحال const يدل restrict. وبعد الجميع const يقول هو أنك لن تكتب من خلال هذا المؤشر ضمن نطاق تلك الوظيفة. وبعد أ const قد لا يزال المؤشر مستعرا. على سبيل المثال النظر في:

int foo( const int *a, int * b )
{
   *b *= 2;
   return *a + *b; // induces LHS: *a must be read back immediately
                   // after write has cleared the store queue
}

بينما لا يمكنك الكتابة مباشرة a في هذه الوظيفة، سيكون قانونيا تماما بالنسبة لك الاتصال به مثل:

int x = 3;
foo( &x, &x );  // returns 12

restrict هو ضمان مختلف: وعد ذلك a != b في جميع المكالمات إلى foo().

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

وعادل التأكيد: هذا هو ليس Arcane أو الأمثل المبكر، إذا كنت تهتم بالأداء على الإطلاق. restrict يمكن أن يؤدي إلى تسريع كبير حقا إذا استخدمت بشكل صحيح.

معظم ما تعرفه خطأ!

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

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

وغني عن القول، "Noalias" لم يصبح جزءا من C. عندما حان الوقت للمحاولة مرة أخرى، فقد كتب الاقتراح بدرجة كافية، حيث تم تضمين تقييده في المعيار - وعلى الرغم من أن Noalias ربما كان اسم أكثر أهمية لذلك، كان هذا الاسم ملوثا للغاية لأنني أشك في أن أي شخص يعتبر محاولة استخدامه.

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

void f(int *a, int *b, int *c) { 
    for (int i=0; i<*a; i++)
        *b += c[i];
}

يريد المترجم حقا وضعي في تسجيل، وتحميل * في سجل، لذلك عندما يحين الوقت لتحديد ما إذا كان سيتم تنفيذ تكرار آخر للحلقة، فإنه يقارن القيم الموجودة في تلك الخاصة ببعضها البعض. لسوء الحظ، لا يمكن أن تفعل ذلك - إذا كان شخص ما استخدم هذه الوظيفة مجنونة تماما، ودعا إليه مع == B، في كل مرة يكتبها إلى * B داخل الحلقة، هذه القيمة الجديدة هي أيضا قيمة * - لذلك يجب أن تقرأ * أ من الذاكرة في كل تكرار للحلقة، فقط في حالة كل من اتصل كان مجنون تماما. باستخدام تقييد يحكي المحول البرمجي، يمكن أن يؤدي إلى إيلاء رمز على افتراض أن A و B سيكون دائما متميزا دائما، لذلك الكتابة إلى * لن يتغير أبدا * B (أو العكس).

فهمك صحيح إلى حد كبير. ال restrict ينص التصفيات على أن البيانات التي يتم الوصول إليها بواسطة مؤشر مؤهل للغاية فقط الوصول إليها هذا المؤشر بالضبط. ينطبق على القراءة كما الآبار كما يكتب.

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

Restrict يحمل أيضا معها تحذير API للبشر بأن وظيفة معينة تنفذ مع افتراض المعلمات غير المستحقة.

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

أخيرا، لاحظ أن المترجم من المحتمل أن يحلل بالفعل التعرجات المحتملة بناء على أنواع البيانات، في مستويات التحسين الأعلى، لذلك restrict من المهم في الغالب للوظائف ذات المؤشرات المتعددة بنفس النوع من البيانات. يمكنك أن تأخذ درسا من هذا الموضوع وتأكد من أن أي التعرجات المتعمدة التي تقوم بها تتم عبر union.

نستطيع ان نرى restrict في العمل:

void move(int *a, int *b) {     void move(int *__restrict a, int *__restrict b) {
    a[0] = b[0];                    a[0] = b[0];
    a[1] = b[0];                    a[1] = b[0];
}                               }
    movl    (%edx), %eax            movl    (%edx), %edx
    movl    %eax, (%ecx)            movl    %edx, (%eax)
    movl    (%edx), %eax            movl    %edx, 4(%eax)
    movl    %eax, 4(%ecx)

في العمود الأيمن، مع restrict, ، لم يحتاج المترجم إلى إعادة توجيه b[0] من الذاكرة . كان قادرا على القراءة b[0] والحفاظ عليه في السجل %edx, ، ثم تخزين السجل مرتين إلى الذاكرة. في العمود الأيسر، لم يكن يعرف ما إذا كان المتجر ل a ربما قد تغيرت b.

ربما يمكن لأي شخص مطلع مع المعيار أن يعطي إجابة أفضل، لكنني سأعطيه طلقة.

"لن يتم تعديل البيانات وراء ظهر مترجم" أصوات أكثر مثل عكس "متقلبة" بالنسبة لي.

"CONST" تعني أن البيانات لن يتم تعديلها أمام المبرمج؛ أي أنها لا تستطيع تعديل البيانات من خلال الموقع الذي تم وضع علامة عليه ك "CONS" (أكتب "التوقيع" لأنه في int const *pi, ، الاسم pi ليس const، ولكن *pi يكون). قد تكون البيانات قابلة للتعديل من خلال مكان آخر (يمكن تمرير بيانات غير ثابت إلى وظيفة كبيانات CONS، بعد كل شيء).

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

قد يكون هذا مثالا من الى ابعد حد المجال الضيق، ولكن منصة NIOS II في Altala هي متحكم ناعم أساسي يمكنك تخصيصها داخل FPGA. ثم، ضمن شفرة المصدر C لهذا الصغرى، يمكنك استخدام أداة C-To-Hardware لتسريع الحلقات الداخلية باستخدام أجهزة مخصصة، بدلا من البرنامج.

هناك، واستخدام __restrict__ الكلمة الرئيسية (وهو نفس c99 restrict) يسمح لأداة C2H بتحسين تسريع الأجهزة بشكل صحيح لعملية المؤشر بالتوازي بدلا من التسلل. على الأقل في هذه الحالة، restrict هو ببساطة ليس يعني للاستهلاك البشري. أنظر أيضا صفحة الشمس على restrict, ، حيث يقول السطر الأول

باستخدام restrict قد تسمح المرافق بشكل مناسب في برامج C بالمترجم بإنتاج ملفات تنفيذية أسرع بكثير.

إذا كان أي شخص مهتما بالقراءة أكثر على C2H، هذا PDF. يناقش تحسين نتائج C2H. القسم في __restrict__ في الصفحة 20.

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