هل يمكنني إجبار تماسك ذاكرة التخزين المؤقت على وحدة المعالجة المركزية X86 Multicore؟

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

سؤال

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

أنا أعرف ال متطايره ستقوم الكلمة الأساسية بفرض متغير للتحديث من الذاكرة، ولكن هل هناك طريقة على معالجات Multicore X86 لإجبار مخابئ جميع النوى لمزامنة؟ هل هذا شيء ما أحتاج إلى القلق عليه، أو سوف متطايره والاستخدام السليم لآليات قفل خفيفة الوزن (كنت أستخدم _interlockedexchange لتعيين متغيرات الأنابيب المتقلبة الخاصة بي) تعامل مع جميع الحالات التي أريد أن أكتبها رمز "قفل مجاني" لمعالجة وحدات المعالجة المركزية X86 Multicore CPUs؟

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

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

المحلول

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

لا يوجد الكثير من تعليمات متساملة ذاكرة التخزين المؤقت في X86. هناك تعليمات جلبية مثل prefetchnta, ، ولكن هذا لا يؤثر على دلالات ترتيب الذاكرة. اعتاد أن يتم تنفيذه عن طريق إحضار القيمة إلى ذاكرة التخزين المؤقت L1 دون تلويث L2، ولكن الأمور أكثر تعقيدا لتصاميم Intel الحديثة ذات مشاركت كبيرة شامل ذاكرة التخزين المؤقت L3.

× 86 وحدة المعالجة المركزية استخدام الاختلاف على بروتوكول ميسي (Mesif for Intel، Moesi for AMD) للحفاظ على مخابئها متماسكة مع بعضها البعض (بما في ذلك مخابئ L1 الخاصة من النوى المختلفة). يتعين على النواة التي تريد كتابة خط ذاكرة التخزين المؤقت إجبار النوى الأخرى على إبطال نسختها قبل أن يتمكن من تغيير نسختها الخاصة من المشاركة إلى الحالة المعدلة.


لا تحتاج إلى أي تعليمات سياج (مثل Mfence) لإنتاج البيانات في مؤشر ترابط واحد وتستهلكها في آخر على X86، لأن الأحمال / المتاجر X86 لديها اكتساب / الافراج عن دلالات مدمج. تحتاج إلى Mfence (حاجز كامل) للحصول على اتساق متتابع. (اقترح إصدار سابق من هذه الإجابة ذلك clflush كان هناك حاجة، وهو غير صحيح).

تحتاج إلى منع تجميع الوقت إعادة ترتيب, ، لأن نموذج ذاكرة C ++ يتم طلبه ضعيف. volatile هو قديم وسيلة سيئة للقيام بذلك؛ C ++ 11 STD :: Atomic هو وسيلة أفضل بكثير لكتابة رمز خالية من القفل.

نصائح أخرى

يتم ضمان تماسك التخزين المؤقت بين النوى بسبب بروتوكول MESI المستخدمة من قبل معالجات X86. ما عليك سوى القلق بشأن تماسك الذاكرة عند التعامل مع الأجهزة الخارجية التي قد تصل إلى الذاكرة بينما لا تزال البيانات تتوافق على مخابئ النوى. لا يبدو وكأنه قضيتك هنا، حيث أن النص يقترح أنك برمجة في userland.

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

إذا كتب Core # 1 إلى متغير، فإنه يبطل كل نسخ أخرى من خط ذاكرة التخزين المؤقت في النوى الأخرى (لأنه يجب أن تحصل عليه الملكية الحصرية من خط ذاكرة التخزين المؤقت قبل ارتكاب المتجر). عندما يقرأ Core # 2 هذا المتغير نفسه، فسوف يفوتك في ذاكرة التخزين المؤقت (ما لم يكتبه Core # 1 بالفعل بقدر مستوى مشترك من ذاكرة التخزين المؤقت).

نظرا لأن خط ذاكرة التخزين المؤقت بأكمله (64 بايت) يجب أن تقرأ من الذاكرة (أو المكتوبة مرة أخرى إلى ذاكرة التخزين المؤقت المشتركة ثم اقرأها Core # 2)، سيكون لها بعض تكلفة الأداء. في هذه الحالة، لا يمكن تجنبها. هذا هو السلوك المطلوب.


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

يمكن تجنب هذه التكلفة عن طريق التأكد من أن هذه المتغيرات ليست في نفس خط ذاكرة التخزين المؤقت. هذا التأثير معروف باسم تقاسم كاذبة نظرا لأنك تجبر المعالجات لمزامنة قيم الكائنات التي لا يتم تقاسمها بالفعل بين المواضيع.

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

أنت لم تحدد المترجم الذي تستخدمه، ولكن إذا كنت على ويندوز، نلقي نظرة على هذه المقالة هنا. وبعد إلقاء نظرة أيضا على المتاحة Sوظائف ynchronization هنا. وبعد قد ترغب في ملاحظة أنه بشكل عام volatile لا يكفي أن تفعل ما تريد القيام به، ولكن تحت VC 2005 و 2008، هناك دلالات غير قياسية تضاف إليها والتي تضيف حواجز الذاكرة الضمنية حول القراءة والكتابة.

إذا كنت تريد أن تكون أشياء محمولة، فستحصل على طريق أصعب بكثير أمامك.

هناك سلسلة من المقالات التي تشرح بها بنيات الذاكرة الحديثة هنا, ، بما فيها مخابئ Intel Core2. والعديد من مواضيع الهندسة المعمارية الحديثة.

المقالات قابلة للقراءة للغاية وموضح جيدا. استمتع !

هناك العديد من الأسئلة الفرعية في سؤالك، لذلك سأجيب عليهم بأفضل حد علمي.

  1. لا يوجد حاليا طريقة محمولة لتنفيذ التفاعلات الخالية من القفل في C ++. اقتراح C ++ 0x يحل هذا عن طريق إدخال مكتبة الذرات الذرية.
  2. المضطرب غير مضمون لتوفير الذرية على متعدد الأطباق وتنفيذها خاص بائع.
  3. في X86، لا تحتاج إلى القيام بأي شيء خاص، باستثناء إعلان المتغيرات المشتركة على أنها متقلبة لمنع بعض التحسينات المترجم التي قد تكسر التعليمات البرمجية متعددة الصدد. يخبر المتقلب المترجم بعدم القيم ذاكرة التخزين المؤقت.
  4. هناك بعض الخوارزميات (Dekker، على سبيل المثال) لن تعمل حتى على X86 مع المتغيرات المتقلبة.
  5. ما لم تكن تعرف بالتأكيد أن الوصول إلى البيانات بين المواضيع بين المواضيع هو عنق الزجاجة الرئيسية في البرنامج، ابتعد عن حلول القفل الخالية من القفل. استخدم بيانات المرور حسب القيمة أو الأقفال.

فيما يلي مقالة جيدة في إشارة إلى استخدام volatile ث / برامج الخيوط.

متقلبة عديمة الفائدة تقريبا للبرمجة متعددة الخيوط.

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

تحرير: إذا كنت تستخدم برنامج Intel Compiler أو GCC، فيمكنك استخدام المانع الذرية, ، والتي يبدو أنها تبذل قصارى جهدها لإقاية ذاكرة التخزين المؤقت عند الإمكان.

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