سؤال

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

تفاصيل هذا السؤال مأخوذة من مشكلة واجهتها عند محاولة استخدام مكتبات ffmpeg مع msvc، ولكن ما يهمني حقًا هو شرح ما هو "محاذاة المكدس".

التفاصيل:

  • عندما يكون برنامج Runnig الخاص بي MSVC الخاص بي والذي يرتبط بالروابط إلى AVCODEC ، أحصل على الخطأ التالي:"لم يقم المترجم بمحاذاة متغيرات المكدس.لقد تم سوء التصرف في Libavcodec "، يليه حادث تصادم في Avcodec.dll.
  • لم يتم تجميع avcodec.dll مع msvc، لذلك لا أستطيع رؤية ما يحدث بالداخل.
  • عند تشغيل ffmpeg.exe واستخدام نفس avcodec.dll، كل شيء يعمل بشكل جيد.
  • لم يتم تجميع ffmpeg.exe مع msvc، وتم الالتزام بـ gcc / mingw (مثل avcodec.dll)

شكرًا،

دان

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

المحلول

محاذاة المتغيرات في الذاكرة (تاريخ قصير).

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

ثم جاءت أجهزة الكمبيوتر 16 بت.بسبب التوافق التنازلي ومشكلات أخرى، تم الاحتفاظ بالبايت 8 بت وتم تقديم الكلمة ذات 16 بت.وكانت كل كلمة 2 بايت.وفي كل دورة على مدار الساعة يمكن معالجة 16 بت من المعلومات.لكن هذا طرح مشكلة صغيرة.

دعونا نلقي نظرة على خريطة الذاكرة:

+----+
|0000| 
|0001|
+----+
|0002|
|0003|
+----+
|0004|
|0005|
+----+
| .. |

يوجد في كل عنوان بايت يمكن الوصول إليه بشكل فردي.ولكن لا يمكن جلب الكلمات إلا من العناوين الزوجية.لذا، إذا قرأنا كلمة عند 0000، فإننا نقرأ البايتات عند 0000 و0001.لكن إذا أردنا قراءة الكلمة في الموضع 0001، فنحن بحاجة إلى وصولين للقراءة.أولًا 0000,0001 ثم 0002,0003 ونحتفظ فقط بـ 0001,0002.

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

على سبيل المثال، إذا كان لدينا بنية تحتوي على حقل بايت (B) وحقل كلمة (W) (ومترجم ساذج جدًا)، فسنحصل على ما يلي:

+----+
|0000| B
|0001| W
+----+
|0002| W
|0003|
+----+

وهو ليس ممتعا.لكن عند استخدام محاذاة الكلمات نجد:

+----+
|0000| B
|0001| -
+----+
|0002| W
|0003| W
+----+

هنا يتم التضحية بالذاكرة من أجل سرعة الوصول.

يمكنك أن تتخيل أنه عند استخدام كلمة مزدوجة (4 بايت) أو كلمة رباعية (8 بايت) يكون هذا أكثر أهمية.لهذا السبب يمكنك مع معظم المترجمين الحديثين اختيار المحاذاة التي تستخدمها أثناء تجميع البرنامج.

نصائح أخرى

تتطلب بعض بنيات وحدة المعالجة المركزية محاذاة محددة لأنواع البيانات المختلفة، وسوف تطرح استثناءات إذا لم تحترم هذه القاعدة.في الوضع القياسي، لا يتطلب x86 هذا لأنواع البيانات الأساسية، ولكن يمكن أن يعاني من عقوبات الأداء (راجع www.agner.org للحصول على نصائح التحسين ذات المستوى المنخفض).

ومع ذلك، SSE تحتوي مجموعة التعليمات (التي تستخدم غالبًا للأداء العالي) لمعالجة الصوت/الفيديو على متطلبات محاذاة صارمة، وستطرح استثناءات إذا حاولت استخدامها على بيانات غير محاذاة (ما لم تستخدم، في بعض المعالجات، إصدارات غير محاذاة أبطأ بكثير).

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

يحرر:أما بالنسبة لسبب حدوث الاستثناء، فمن المحتمل أن يكون هناك روتين في مكتبة الارتباط الحيوي (DLL) يريد استخدام تعليمات SSE على بعض بيانات المكدس المؤقتة، ويفشل لأن المترجمين المختلفين لا يتفقان على اصطلاحات الاتصال.

IIRC، محاذاة المكدس هي عندما يتم وضع المتغيرات على المكدس "محاذاة" لعدد معين من البايتات.لذا، إذا كنت تستخدم محاذاة مكدس 16 بت، فسيبدأ كل متغير في المكدس من بايت يمثل مضاعف 2 بايت من مؤشر المكدس الحالي داخل الوظيفة.

هذا يعني أنه إذا كنت تستخدم متغيرًا أقل من 2 بايت، مثل char (1 بايت)، فسيكون هناك 8 بتات من "الحشوة" غير المستخدمة بينه وبين المتغير التالي.يسمح هذا ببعض التحسينات مع افتراضات تعتمد على مواقع متغيرة.

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

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

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

// Some compilers won't align this as it's on the stack...
int __declspec(align(32)) needsToBe32Aligned = 0;
// Change to
static int __declspec(align(32)) needsToBe32Aligned;
needsToBe32Aligned = 0;

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

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