سؤال

يستخدم Linux kernel lock; addl $0,0(%%esp) كحاجز للكتابة ، بينما تستخدم مكتبة RE2 xchgl (%0),%0 كحاجز كتابة. ما هو الفرق وأيها أفضل؟

هل يتطلب X86 أيضًا تعليمات حاجز القراءة؟ يعرّف RE2 وظيفة حاجز القراءة على أنها عدم وجود OP على x86 بينما يعرّف Linux ذلك أيضًا lfence أو عدم وجود op اعتمادًا على ما إذا كان SSE2 متاحًا. متى lfence مطلوب؟

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

المحلول

ال "قفل؛ addl $ 0،0 (٪٪ ESP)"أسرع في حالة اختبار حالة 0 حالة قفل على عنوان (٪٪ ESP). لأننا نضيف قيمة 0 إلى متغير القفل وتم تعيين علامة الصفر على 1 إذا كانت قيمة قفل المتغير في العنوان (٪٪ ESP ) هو 0.


lfence من ورقة بيانات Intel:

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

(ملحوظة المحرر: mfence أو أ lockعملية ED هي السياج الوحيد المفيد (بعد المتجر) للتناسق المتسلسل. lfence يفعل ليس كتلة storeload إعادة ترتيب من قبل المخزن العازلة المتجر.)


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


لذا فإن الفرق الرئيسي بين هاتين التعليمات هو ذلك xchgl لن يكون للتعليمات أي تأثير على الأعلام الشرطية. بالتأكيد يمكننا اختبار حالة متغير القفل مع قفل CMPXCHG التعليمات ولكن هذا لا يزال أكثر تعقيدًا من القفل أضف $ 0 تعليمات.

نصائح أخرى

نقلاً عن أدلة IA32 (المجلد 3A ، الفصل 8.2: ترتيب الذاكرة):

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

  • لا يتم إعادة ترتيب القراءات بقراءات أخرى
  • لا يتم إعادة ترتيب الكتابة بقراءات أقدم
  • لا يتم إعادة ترتيب المكتب إلى الذاكرة مع عمليات الكتابة الأخرى ، باستثناء
    • يكتب مع CLFLUSH تعليمات
    • تم تنفيذ متاجر البث (يكتب) مع تعليمات الحركة غير الزملية ([قائمة التعليمات هنا])
    • عمليات السلسلة (انظر القسم 8.2.4.1)
  • قد يتم إعادة ترتيب القراءات مع كتابة كبار السن إلى مواقع مختلفة ولكن ليس مع كتابة كبار السن إلى نفس الموقع.
  • لا يمكن إعادة ترتيب القراءات أو الكتابة بتعليمات الإدخال/الإخراج أو الإرشادات المقفلة أو تعليمات التسلسل
  • القراءات لا يمكن أن تمر LFENCE و MFENCE تعليمات
  • لا يمكن أن تمر الكتابة SFENCE و MFENCE تعليمات

ملاحظة: "في نظام معالج واحد" أعلاه مضلل قليلاً. نفس القواعد التي تحتفظ بكل معالج (منطقي) بشكل فردي ؛ ثم يمضي الدليل لوصف قواعد الطلب الإضافية بين المعالجات المتعددة. الشيء الوحيد حوله المتعلق بالسؤال هو ذلك

  • التعليمات المقفلة لها ترتيب إجمالي.

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

أيضًا ، في ذاكرة الكتابة ، لا يتم إعادة ترتيب القراءات أبدًا ، لذلك ليست هناك حاجة لحواجز القراءة. تحتوي معالجات X86 الحديثة على نموذج تناسق للذاكرة أضعف لتدفق المتاجر والذاكرة المملوءة بالكتابة (شائع الاستخدام لذاكرة الرسومات المعينة). هذا هو المكان مختلف fence تعليمات تلعب دورها ؛ إنها ليست ضرورية لأي نوع من الذاكرة الأخرى ، ولكن بعض برامج التشغيل في Linux kernel تتعامل مع الذاكرة المُمبأة في الكتابة ، لذا فقد حددوا للتو حاجز القراءة بهذه الطريقة. قائمة نموذج الطلب لكل نوع ذاكرة في القسم 11.3.1 في المجلد. 3A من كتيبات IA-32. نسخة قصيرة: تسمح الكتابة والكتابة والكتابة المحمية للكتابة بقراءات مضاربة (باتباع القواعد على النحو المفصل أعلاه) ، والذاكرة التي لا يمكن أن تكون قابلة للاشمئزاز لا يمكن أن تكون قابلة للحياة لها ضمانات طلب قوية (لا يتم إعادة ترتيب المعالج ، وقراءات/كتابة على الفور ، وتستخدم في MMIO ) وكتابة الذاكرة المشتركة لها ترتيب ضعيف (أي قواعد الطلب المريحة التي تحتاج إلى أسوار).

lock addl $0, (%esp) هو بديل ل mfence, ، ليس lfence.

تكون حالة الاستخدام عندما تحتاج إلى منع إعادة ترتيب storeload (النوع الوحيد الذي يسمح به نموذج الذاكرة القوي لـ X86) ، لكنك لا تحتاج إلى عملية RMW الذرية على متغير مشترك. https://preshing.com/20120515/memory-reDordering-

على سبيل المثال افتراض محاذاة std::atomic<int> a,b:

movl   $1, a             a = 1;    Atomic for aligned a
# barrier needed here
movl   b, %eax           tmp = b;  Atomic for aligned b

خياراتك هي:

  • قم بعمل متجر متتابع مع xchg, ، على سبيل المثال mov $1, %eax / xchg %eax, a لذلك لا تحتاج إلى حاجز منفصل ؛ إنه جزء من المتجر. أعتقد أن هذا هو الخيار الأكثر فعالية على معظم الأجهزة الحديثة ؛ C ++ 11 مترجم آخر غير استخدام GCC xchg لمخازن seq_cst.
  • يستخدم mfence كحاجز. (يستخدم GCC mov + mfence لمخازن seq_cst).
  • يستخدم lock addl $0, (%esp) كحاجز. أي lockتعليمات ED هي حاجز كامل. هل لدى Lock XCHG نفس السلوك مثل Mfence؟

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

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

عند الإمكان ، باستخدام xchg من الأفضل بالنسبة لمتجر SEQ-CST الأفضل ، على الرغم من أنه يقرأ أيضًا من الموقع المشترك. mfence هو أبطأ من المتوقع في وحدة المعالجة المركزية الأخيرة لإنتل (هل الأحمال والمتاجر هي الإرشادات الوحيدة التي يتم إعادة ترتيبها؟) ، أيضا منع التنفيذ خارج الترتيب لتعليمات غير ذاكرة مستقلة بنفس الطريقة lfence يفعل.

قد يكون من المفيد الاستخدام lock addl $0, (%esp)/(%rsp) بدلاً من mfence حتى عندما mfence متاح ، لكنني لم أجرب الجوانب السلبية. استخدام -64(%rsp) أو قد يجعل شيء ما أقل عرضة لإطالة اعتماد البيانات على شيء ساخن (عنوان محلي أو عودة) ، ولكن يمكن أن يجعل أدوات مثل Valgrind غير سعيدة.


lfence ليس مفيدًا أبدًا لطلب الذاكرة إلا إذا كنت تقرأ من ذاكرة الوصول العشوائي للفيديو (أو بعض منطقة WC الأخرى التي تم طلبها بشكل ضعيف) مع أحمال MovNtDQA.

التسلسل التنفيذي خارج الترتيب (ولكن ليس المخزن المؤقت للمخزن) ليس مفيدًا لإيقاف ترتيب إعادة ترتيب storeload (النوع الوحيد الذي يسمح به نموذج الذاكرة القوي في X86 لمناطق ذاكرة WB العادية).

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

أنظر أيضا متى يجب أن أستخدم _mm_sfence _mm_lfence و _mm_mfence (إجابتي وإجابة @Beeonrope) لمعرفة المزيد عن السبب lfence ليس مفيدًا ، ومتى تستخدم كل من تعليمات الحاجز. (أو في الألغام ، الجوهارات C ++ عند البرمجة في C ++ بدلاً من ASM).

جانبا للإجابات الأخرى ، وجدت نقطة الساخنة ذلك lock; addl $0,0(%%esp) مع إزاحة صفرية قد لا تكون مثالية ، على بعض المعالجات يمكن أن تقديم تبعيات بيانات خاطئة; ؛ ذات صلة JDK علة.

يمكن أن يؤدي لمس موقع المكدس مع إزاحة مختلفة إلى تحسين الأداء في بعض الحالات.

الجزء المهم من lock; addl و xchgl هل lock اختصار. إنه ضمني ل xchgl. لا يوجد فرق بين الاثنين. كنت أنظر إلى كيفية تجميعهم واختيار واحد أقصر (بالبايت) لأن هذا عادة ما يكون أسرع للعمليات المكافئة على x86 (وبالتالي الحيل مثل xorl eax,eax)

ربما يكون وجود SSE2 مجرد وكيل للحالة الحقيقية التي هي في نهاية المطاف وظيفة cpuid. ربما اتضح أن SSE2 يعني وجود lfence وتم فحص/توافر SSE2/تخزين مؤقت في الحذاء. lfence مطلوب عندما يكون متاحًا.

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