خطر داده چیست
در یک پردازنده پایپلاینشده، چند دستور بهطور همزمان در حال پیشرفت هستند. یک Data Hazard زمانی رخ میدهد که یک دستور نیاز دارد از مقداری استفاده کند که یک دستور قبلی، که هنوز در حال حرکت در پایپلاین است، هنوز محاسبهاش را تمام نکرده و آن را به فایل رجیستر write back نکرده است.
این توالی از دستورات را در نظر بگیرید:
add a, b, c
sub d, a, eدستور دوم به مقدار a نیاز دارد، اما دستور اول تا زمانی که دستور دوم به decode میرسد هنوز به مرحله write-back خودش نرسیده است. بدون هیچ تصحیحی، دستور دوم یک مقدار قدیمی و بیاعتبار از a را از فایل رجیستر میخواند.
راهحل اول: Forwarding
Forwarding (که Bypassing نیز نامیده میشود) این مسئله را بدون از دست دادن هیچ سیکل ساعتی حل میکند، با افزودن سیمکشی اضافه که یک نتیجه را مستقیماً از جایی که محاسبه میشود به جایی که نیاز است مسیریابی میکند، و کاملاً از مسیر معمول از طریق فایل رجیستر عبور میکند.
بدون forwarding:
add نتیجه را محاسبه میکند → در فایل رجیستر نوشته میشود (سیکل ۵)
sub به نتیجه نیاز دارد → از فایل رجیستر خوانده میشود (سیکل ۳) — خیلی زود، مقدار نادرست
با forwarding:
add نتیجه را در مرحله EX محاسبه میکند (سیکل ۳)
نتیجه مستقیماً به مرحله EX دستور sub فوروارد میشود (سیکل ۴)از آنجا که نتیجهای که دستور دوم نیاز دارد درست پس از مرحله execute دستور اول در دسترس است، مسیرهای سختافزاری اضافی میتوانند آن مقدار را بهموقع به جلو تغذیه کنند تا دقیقاً وقتی دستور دوم نیاز دارد به آن برسد، و از هر سیکل هدررفته جلوگیری میکنند.
وقتی Forwarding بهتنهایی کافی نیست
Forwarding بیشتر خطرهای داده را حل میکند، اما نه همه آنها را. یک Load-Use Hazard بهطور خاص زمانی رخ میدهد که دستوری بلافاصله پس از یک load به مقداری نیاز دارد که آن load در حال بازیابی آن است:
ld a, 0(b)
add d, a, eمقدار loadشده تا پایان مرحله دسترسی به حافظه در دسترس نیست، که دیرتر از نقطهای اتفاق میافتد که مرحله execute دستور بعدی به آن نیاز دارد، حتی با وجود سیمکشی forwarding. در این مورد خاص، forwarding نمیتواند مقدار را بهموقع تحویل دهد.
راهحل دوم: Stalling
وقتی forwarding نمیتواند یک خطر را بهموقع حل کند، پایپلاین باید یک Stall (که Bubble نیز نامیده میشود) وارد کند: دستور وابسته، و هر چیزی پشت سر آن، برای یک سیکل ساعت در جای خود نگه داشته میشود تا وقتی مقدار مورد نیاز در دسترس شود، به قیمت یک سیکل توان عملیاتی هدررفته.
ld a, 0(b) : IF ID EX MEM WB
[حباب وارد شده]
add d, a, e : IF ID -- EX MEM WBاین کار عمداً یک سیکل را هدر میدهد بهجای تولید یک نتیجه نادرست، که همیشه مبادله درست است، چون دقت نمیتواند به قیمت سرعت فدا شود.
چرا این تمایز برای طراحی کامپایلر اهمیت دارد
از آنجا که توقفهای load-use بهطور خاص یک سیکل هزینه دارند، کامپایلرهایی که این خطر را میفهمند گاهی میتوانند دستورات مستقل را طوری بازچینی کنند که بلافاصله پس از یک load قرار گیرند، و آنچه در غیر این صورت یک سیکل توقف هدررفته بود را با کار مفید پر کنند — تکنیکی به نام Instruction Scheduling، که مستقیماً به درک علت دقیق این خطر متکی است.