No history yet

معماری پردازنده و موازات

پردازش موازی در سطح دستورالعمل

پردازنده‌های مدرن برای رسیدن به سرعت‌های بالا، تنها به افزایش سرعت کلاک اتکا نمی‌کنند. در عوض، آن‌ها از یک استراتژی هوشمندانه به نام موازات در سطح دستورالعمل (ILP) استفاده می‌کنند. ایده اصلی این است که به جای اجرای دستورالعمل‌ها به صورت کاملاً متوالی، چندین دستورالعمل را همزمان در مراحل مختلف اجرا کنیم. این کار شبیه به یک خط مونتاژ کارخانه است که در آن هر ایستگاه، بخشی از کار را به طور همزمان با ایستگاه‌های دیگر انجام می‌دهد.

ستون فقرات این روش، خط لوله یا Pipelining است. ما قبلاً با اصول اولیه آن آشنا شده‌ایم: تقسیم اجرای یک دستورالعمل به مراحل کوچکتر (مانند واکشی، رمزگشایی، اجرا، دسترسی به حافظه و نوشتن نتیجه). اما در دنیای واقعی، این فرآیند با چالش‌هایی به نام ** مخاطرات (Hazards)** روبرو می‌شود که می‌توانند این خط مونتاژ را متوقف کنند. این مخاطرات به سه دسته اصلی تقسیم می‌شوند: ساختاری، داده‌ای و کنترلی.

مدیریت مخاطرات در خط لوله

مخاطرات داده‌ای (Data Hazards) زمانی رخ می‌دهند که یک دستورالعمل به نتیجه دستورالعمل قبلی خود نیاز دارد که هنوز آماده نشده است. برای مثال، دستور ADD R1, R2, R3 نتیجه‌ای تولید می‌کند که ممکن است بلافاصله در دستور بعدی SUB R4, R1, R5 مورد نیاز باشد. اگر پردازنده منتظر بماند تا ADD تمام مراحل خود را طی کند، خط لوله متوقف می‌شود و کارایی کاهش می‌یابد.

برای حل این مشکل، تکنیکی به نام Forwarding یا Bypassing به کار می‌رود. به جای اینکه منتظر بمانیم تا نتیجه در رجیستر نوشته شود، واحد اجرا نتیجه را مستقیماً به ورودی مرحله اجرای دستورالعمل بعدی «ارسال» می‌کند. این کار یک میانبر سخت‌افزاری ایجاد می‌کند که از توقف‌های غیرضروری جلوگیری می‌کند.

Forwarding مانند این است که در خط مونتاژ، یک کارگر قطعه‌ای را که تازه تمام کرده، مستقیماً به دست همکار بعدی خود بدهد، به جای اینکه آن را روی نوار نقاله بگذارد تا به انتهای خط برسد و سپس برگردد.

نوع دیگر مخاطره، مخاطره کنترلی (Control Hazard) است که به دلیل دستورالعمل‌های پرش (Branch) به وجود می‌آید. وقتی پردازنده با یک پرش شرطی (مانند BEQ - Branch if Equal) مواجه می‌شود، نمی‌داند که آیا باید به آدرس پرش برود یا دستورالعمل بعدی را اجرا کند تا زمانی که شرط بررسی شود. این تردید باعث توقف خط لوله می‌شود.

راه‌حل این مشکل، پیش‌بینی پرش (Branch Prediction) است. پردازنده بر اساس تاریخچه اجراهای قبلی، «حدس» می‌زند که نتیجه پرش چه خواهد بود و به صورت خوشبینانه، دستورالعمل‌های مسیر محتمل‌تر را واکشی و اجرا می‌کند. اگر حدس درست باشد، هیچ زمانی تلف نمی‌شود. اگر اشتباه باشد، پردازنده نتایج اجراهای اشتباه را پاک کرده و از مسیر صحیح ادامه می‌دهد. این اجرای خوشبینانه، اجرای تخمینی نامیده می‌شود.

اجرای خارج از نوبت و معماری Superscalar

حتی با وجود Forwarding، گاهی اوقات دستورالعمل‌ها به دلیل وابستگی به داده‌هایی که از حافظه اصلی خوانده می‌شوند (که بسیار کند است)، متوقف می‌شوند. برای جلوگیری از بیکار ماندن پردازنده، معماری‌های پیشرفته از اجرای خارج از نوبت (Out-of-Order Execution - OoOE) استفاده می‌کنند. در این روش، پردازنده به دنبال دستورالعمل‌های مستقلی در ادامه کد می‌گردد که آماده اجرا هستند و آن‌ها را زودتر از دستورالعمل‌های متوقف شده اجرا می‌کند. این کار شبیه به انجام کارهای دیگر در لیست خریدتان است در حالی که منتظر آماده شدن یک سفارش در فروشگاه هستید.

یکی از الگوریتم‌های کلاسیک برای پیاده‌سازی OoOE، الگوریتم توماسولو است. این الگوریتم از تکنیکی به نام تغییر نام رجیستر (Register Renaming) استفاده می‌کند تا وابستگی‌های کاذب داده‌ای را از بین ببرد و به دستورالعمل‌های بیشتری اجازه دهد تا به صورت موازی اجرا شوند. الگوریتم توماسولو به واحدهای اجرایی مختلف اجازه می‌دهد تا به طور مستقل و به محض آماده شدن داده‌هایشان، کار خود را شروع کنند.

Lesson image

برای بهره‌برداری کامل از ILP، پردازنده‌ها باید بتوانند در هر چرخه ساعت، بیش از یک دستورالعمل را واکشی و صادر کنند. این ویژگی، اساس معماری Superscalar است. یک پردازنده سوپراسکالر دارای چندین واحد اجرایی موازی است (مثلاً دو واحد محاسبه و منطق، یک واحد ممیز شناور و غیره). یک بخش پیچیده به نام «واحد صدور» (Issue Unit) تصمیم می‌گیرد که کدام دستورالعمل‌ها می‌توانند به طور ایمن و همزمان به این واحدهای اجرایی ارسال شوند.

ویژگیمعماری Scalarمعماری Superscalar
دستورالعمل در هر چرخهحداکثر ۱بیش از ۱
واحدهای اجرایییک عدد از هر نوعچندین واحد اجرایی موازی
پیچیدگی سخت‌افزارکمتربسیار بیشتر (نیازمند منطق صدور پیچیده)
پتانسیل ILPمحدودبالا

RISC در مقابل CISC: تأثیر بر بهینه‌سازی

دو فلسفه اصلی در طراحی مجموعه دستورالعمل‌های پردازنده (ISA) وجود دارد: CISC (Complex Instruction Set Computer) و RISC (Reduced Instruction Set Computer).

پردازنده‌های CISC، مانند خانواده x86 اینتل، دارای مجموعه دستورالعمل‌های بزرگ و پیچیده‌ای هستند. یک دستورالعمل CISC ممکن است چندین کار سطح پایین را ترکیب کند، مانند خواندن از حافظه، انجام یک عملیات ریاضی و نوشتن نتیجه در حافظه، همه در یک دستور. هدف اولیه این بود که کار کامپایلرها ساده‌تر شود و حجم کد کاهش یابد.

در مقابل، پردازنده‌های RISC، مانند معماری ARM (که در اکثر گوشی‌های هوشمند استفاده می‌شود) و MIPS، فلسفه متفاوتی دارند. آن‌ها مجموعه دستورالعمل‌های کوچکتر و ساده‌تری دارند. هر دستورالعمل یک کار ساده و مشخص انجام می‌دهد. عملیات پیچیده باید توسط دنباله‌ای از دستورالعمل‌های ساده ساخته شوند.

این سادگی مزایای مهمی دارد: دستورالعمل‌ها سریع‌تر رمزگشایی می‌شوند و پیاده‌سازی تکنیک‌های پیشرفته‌ای مانند خط لوله عمیق (deep pipelining) و Superscalar را آسان‌تر می‌کنند. در معماری RISC، بار بهینه‌سازی بیشتر بر دوش کامپایلر است. کامپایلر باید کد سطح بالا را به دنباله‌ای بهینه از دستورالعمل‌های ساده ترجمه کند و ترتیب آن‌ها را طوری تنظیم کند که حداکثر کارایی از خط لوله استخراج شود. این فرآیند زمان‌بندی دستورالعمل (Instruction Scheduling) نام دارد.

این ایده که پیچیدگی را از حوزه نرم‌افزار به حوزه سخت‌افزار منتقل کنیم، ایده محرک پشت CISC است و تقریباً هر کاری که یک ماشین CISC واقعی انجام می‌دهد، با این هدف است.

درک این معماری‌ها برای برنامه‌نویسی سطح پایین و بهینه‌سازی کامپایلر حیاتی است. یک برنامه‌نویس اسمبلی یا یک توسعه‌دهنده کامپایلر باید بداند که چگونه ترتیب دستورالعمل‌ها را برای به حداقل رساندن توقف‌های خط لوله و استفاده حداکثری از واحدهای اجرایی موازی، بچیند.

Quiz Questions 1/5

کدام تکنیک به پردازنده اجازه می‌دهد تا دستورالعمل‌های مستقل بعدی را در حالی که یک دستورالعمل به دلیل وابستگی به داده‌های حافظه متوقف شده است، اجرا کند؟

Quiz Questions 2/5

در معماری RISC (Reduced Instruction Set Computer)، وظیفه بهینه‌سازی و ترتیب‌دهی دستورالعمل‌ها برای حداکثر کارایی خط لوله (Pipelining) عمدتاً بر عهده کیست؟

این تکنیک‌ها، از پیش‌بینی پرش گرفته تا اجرای خارج از نوبت، همگی با هم کار می‌کنند تا اطمینان حاصل شود که پردازنده شما تا حد امکان به صورت کارآمد عمل می‌کند و در هر لحظه، محاسبات مفیدی را انجام می‌دهد.