پیش‌واکشی حافظه نهان

**پیش‌واکشی حافظه نهان** ، تکنیکی است که توسط پردازنده‌های رایانه انجام می‌شود و برای تقویت عملکرد اجرای برنامه‌ها است. در این تکنیک، پردازنده دستورالعمل‌ها یا داده‌ها را از حافظه اصلی (حافظه کندتر) به یک حافظه محلی (حافظه تندتر) قبل از اینکه واقعاً مورد نیاز باشد بازیابی می‌کند (از این رو اصطلاح 'prefetch' نامیده می‌شود). اکثر پردازنده‌های کامپیوتری مدرن، دارای حافظه نهان سریع و محلی هستند که داده‌های بازیابی شده را تا زمانی که مورد استفاده قرار نگرفته‌اند در خود نگه می‌دارند. معمولاً عملیات پیش واکشی در حافظه اصلی صورت می‌گیرد. به دلیل طراحی پردازنده‌ها، معمولاً دسترسی به حافظه‌های نهان بسیار سریع‌تر از دسترسی به حافظه اصلی است، بنابراین واکشی اولیه داده‌ها و سپس دسترسی به آن‌ها از طریق حافظه نهان معمولاً چندین مرتبه سریع‌تر از دسترسی مستقیم به آن‌ها از حافظه اصلی است. واکشی اولیه را می‌توان با دستورالعمل‌های کنترل حافظه پنهان غیر مسدود کننده (non-blocking) انجام داد.

## مقایسه پیش واکشی حافظه نهان در داده‌ها با دستورالعمل‌ها
پیش واکشی حافظه نهان می‌تواند داده‌ها یا دستورالعمل‌ها را در حافظه پنهان بازیابی کند.
* **پیش** واکشی داده، داده‌ها را قبل از نیاز بازیابی می‌کند. از آنجا که الگوهای دسترسی به داده‌ها نظم کمتری نسبت به الگوهای دستورالعمل نشان می‌دهند، واکشی داده‌ها معمولاً چالش‌برانگیزتر از واکشی دستورالعمل است.
* **پیش واکشی** دستورالعمل، دستورالعمل‌ها را قبل از نیاز به اجرا بازیابی می‌کند. اولین ریزپردازنده‌های اصلی که از نوعی پیش واکشی دستورالعمل استفاده کردند، Intel 8086 (شش بایت) و موتورولا ۶۸۰۰۰ (چهار بایت) بودند. در سال‌های اخیر، تمام پردازنده‌های با کارایی بالا از تکنیک‌های پیش واکشی استفاده می‌کنند.

## مقایسه پیش واکشی حافظه نهان سخت‌افزار با نرم‌افزار
پیش واکشی حافظه نهان می‌تواند توسط سخت‌افزار یا نرم‌افزار انجام شود.
* **پیش واکشی مبتنی بر سخت‌افزار** معمولاً با داشتن مکانیزم سخت‌افزاری اختصاصی در پردازنده انجام می‌شود. در این روش، دستورالعمل‌ها یا داده‌هایی را که توسط برنامه اجراکننده در یک جریان خاص (stream) درخواست می‌شود، بررسی می‌شود و چند دستور یا دادهٔ بعدی که ممکن است در این جریان برنامه به آن نیاز داشته باشد شناسایی می‌کند و در حافظه پنهان پردازنده، پیش واکشی می‌کند.
* **پیش واکشی مبتنی بر نرم‌افزار** معمولاً با تجزیه و تحلیل کد توسط کامپایلر و درج دستورالعمل‌های اضافی «پیش واکشی» در برنامه هنگام کامپایل شدن انجام می‌شود.

## روش‌های پیش واکشی سخت‌افزاری
### بافرهای جریان (Stream buffers)
* بافرهای جریان بر اساس مفهوم "بلوک پیش بینی (OBL)" پیشنهاد شده توسط آلن جی اسمیت توسعه یافتند.
* بافرهای جریان یکی از رایج‌ترین تکنیک‌های پیش واکشی مبتنی بر سخت‌افزار هستند. این تکنیک در ابتدا توسط نورمن جوپی در سال 1990 ابداع شد و از آن زمان تاکنون انواع مختلفی از این روش ***ارائه*** شده‌است. ایده اصلی این است که آدرس حافظه پنهان (و k آدرس‌های بعدی) در بافر جداگانه‌ای با عمق k بازیابی می‌شوند. این بافر، بافر جریان نامیده می‌شود و از حافظه نهان جدا است. در صورتی که آدرس ذخیره شده در بلوک‌ها، با آدرسی که برنامه درحال اجرا روی پردازنده درخواست کرده مطابقت داشته باشد، پردازنده، آدرس مورد نیاز برنامه را از طریق بافر جریان در اختیار برنامه می‌دهد (که این فضا برای انجام دستورالعمل یا ذخیره داده استفاده می‌شود). شکل زیر این تنظیمات را نشان می‌دهد:

جایگزین=A typical stream buffer setup as originally proposed|بندانگشتی| یک بافر جریان مشابه با بافری که در ابتدا توسط نورمن جوپی در سال ۱۹۹۰ پیشنهاد شد.
* هر زمان که مکانیزم پیش واکشی بخواهد بلوکی مثل A را از سیستم پیش واکشی کند، یک بافر جریان برای پیش واکشی چند بلوک متوالی ایجاد می‌کند؛ مثلاً اگر بافر جریان بتواند ۴ بلوک را نگه دارد، A+1، A+2، A+3، A+4 را پیش واکشی می‌کنیم و آن‌ها را در بافر جریان اختصاص داده شده نگه می‌داریم. اگر پردازنده در مرحله بعدی A+1 را مصرف کند، باید از بافر جریان به مکان بالاتر از A+1 در حافظه نهان پردازنده منتقل شود و اولین ورودی بافر جریان A+2 خواهد بود. این الگوی پیش واکشی، **پیش واکشی متوالی** نامیده می‌شود و عمدتاً در مواقعی استفاده می‌شود که قرار است عملیات پیش واکشی روی بلوک‌های مجاور انجام شود. به عنوان مثال، هنگام پیش واکشی دستورالعمل‌ها از این مکانیزم استفاده می‌شود.
* این مکانیسم را می‌توان با افزودن چندین بافر جریان توسعه داد که هر کدام، یک جریان پیش واکشی جداگانه را حفظ می‌کنند. برای پیش واکشی هر داده جدید، یک بافر جریان جدید تخصیص داده می‌شود و هر کدام، به روشی مشابه که در بالا توضیح داده شد عمل می‌کنند.
* اندازه ایده‌آل بافر از طریق آزمایش کردن معیارهای مختلفو به بقیه ریزمعماری درگیر بستگی دارد.

روش دیگری برای پیش واکشی دستورالعمل‌ها، پیش واکشی تعدادی آدرس به صورت دنباله هستند. این روش عمدتاً زمانی استفاده می‌شود که بلوک‌های متوالی که قرار است از قبل واکشی شوند، دارای s آدرس جدا از هم هستند،این روش **Strided Prefetching** نام دارد.

## روش‌های پیش واکشی نرم‌افزاری
### پیش واکشی هدایت شده توسط کامپایلر
پیش واکشی هدایت شده توسط کامپایلر، در حلقه‌هایی با تعداد زیاد تکرار استفاده می‌شود. در این تکنیک، کامپایلر cache misses را پیش‌بینی می‌کند و یک دستورالعمل پیش واکشی بر اساس جریمه خطا و زمان اجرای دستورالعمل‌ها ایجاد می‌کند.

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

یکی از مزیت‌های اصلی پیش واکشی نرم‌افزار، کاهش تعداد cache misses است.

مثال زیر نشان می‌دهد که چگونه یک دستورالعمل پیش واکشی به یک کد برای بهبود عملکرد حافظه پنهان اضافه می‌شود.

یک حلقه for را در نظر بگیرید:
```c++
for (int i=0; i<1024; i++) {
  array1[i] = 2 * array1[i];
}
```
در هر تکرار، i امین عنصر از آرایه "array1" قابل دسترسی است؛ بنابراین، می‌توانیم عناصری را که در تکرارهای بعدی به آن نیاز داریم را با قرار دادن یک دستورالعمل "prefetch" مانند شکل زیر پیش واکشی کنیم:
```c++
for (int i=0; i<1024; i++) {
  prefetch (array1 [i + k]);
  array1[i] = 2 * array1[i];
}
```
اینجا، گام پیش واکشی، k به دو عامل بستگی دارد، cache miss penalty و زمان لازم برای اجرای یک تکرار حلقه ***for.*** به عنوان مثال، اگر یک تکرار از حلقه ۷ سیکل طول بکشد تا اجرا شود، cache miss penalty چهل و نه چرخه باشد. داریمk = 49/7 = 7. که به این معنی است که باید ۷ عنصر را پیش واکشی کنیم. در زمان اولین تکرار، i=۰ خواهد بود، بنابراین عنصر هفتم را پیش واکشی می‌کنیم. با همین منوال، ۷ عنصر اول (i=۰->۶) همچنان پیش واکشی می‌شوند (به عبارتی دیگر هر عنصر از آرایه۱ در یک خط‌کش برای خودش قرار دارد).

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

## معیارهای پیش واکشی حافظه پنهان
سه معیار اصلی برای پیش واکشی حافظه پنهان وجود دارد

### پوشش
پوشش، نسبت تعداد cache misses پیشگیری شده به خاطر پیش واکشی به تعداد کل cache misses است:

Coverage = \frac{\text{Cache Misses eliminated by Prefetching}}{\text{Total Cache Misses}} ،

جایی که، \text{Total Cache Misses} = (\text{Cache misses eliminated by prefetching}) + (\text{Cache misses not eliminated by prefetching})

### دقت
دقت، نسبت پیش واکشی مفید (آدرس‌های پیش واکشی شده توسط برنامه) به کل پیش واکشی‌ها.

\text{Prefetch Accuracy} = \frac{\text{Cache Misses eliminated by prefetching}}{(\text{Useless Cache Prefetches}) + (\text{Cache Misses eliminated by prefetching})}

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

### به موقع بودن
گوییم یک پیش واکشی به موقع است اگر حدفاصل زمان بین پیش واکشی و واکشی کم باشد. یک مثال برای توضیح بیشتر به موقع بودن به شرح زیر است:

یک حلقه for را در نظر بگیرید که در آن هر تکرار ۳ چرخه برای اجرا و عملیات پیش واکشی ۱۲ چرخه طول می‌کشد. این بدان معناست که برای اینکه داده‌های پیش واکشی شده مفید باشند، باید پیش واکشی را 12/3 = 4  تکرار قبل از استفاده اصلی انجام دهیم.

## جستارهای وابسته
* پیش واکشی ورودی صف
* پیش واکشی پیوند
* پیش واکش کننده
* دستورالعمل کنترل کش

## منابع

رده:حافظه نهان