حافظه کش چیست و نحوه عملکرد آن چگونه است؟
پردازنده هر رایانه ای -از یک لپتاپ ارزان گرفته تا یک سرور چند میلیون دلاری- دارای حافظه پنهان یا به عبارتی کش «Cache» است. این حافظه از سطوح مختلفی تشکیل شده است. اما حقیقتا کش چیست و چه تاثیری بر عملکرد پردازنده دارد؟ این سوالی است که در این مقاله به آن پرداخته شده و تلاش میشود ابعاد مختلف ماجرا به زبانی ساده بیان شود.
حافظه پنهان دقیقا چیست؟
کش، حافظهای بسیار سریع است که دقیقا در کنار واحدهای منطقی پردازنده قرار دارد. البته این تعریف نیمخطی نمیتواند به خوبی بیانگر عملکرد این حافظه باشد، پس بیایید دقیقتر به مسئله بپردازیم.
تصور کنید یک حافظه جادویی داریم که سرعت و ظرفیتی بینهایت دارد! اینقدر سریع است که میتواند به تعداد نامحدود، تراکنشهای داده را همزمان انجام داده و همیشه دادهها را بیخطر و امن نگهداری کند. البته طوری تصور نکنید که این حافظه از راه دور با پردازنده در ارتباط داشته باشد! چون فرضی فرا جادویی میشود که طراحی پردازندهها را بسیار سادهتر میکند.
در این صورت پردازندهها فقط باید واحدهای منطقی برای عملیات جمع، ضرب و. . و سیستمی برای مدیریت انتقال دادهها داشته باشد. زیرا حافظه جادویی ما بینهایت سرعت و ظرفیت دارد که میتواند تمام شمارههای مورد نیاز را فورا ارسال و دریافت کند. و در این صورت هیچ یک از واحدهای منطقی پردازنده منتظر تراکنش داده نمیشود.
اما همانطور که میدانید چنین فناوری جادویی وجود ندارد. ما درایوهای سخت و جامد داریم که حتی بهترین آنها هم قادر به انجام تمام انتقال دادههای مورد نظر برای یک پردازنده معمولی هم نیست!
دلیل این اتفاق و ناتوانی درایوهای سخت این است که پردازندههای مدرن فوقالعاده سریع هستند! آنها در یک کلاک (clock) توانایی جمع دو عدد صحیح ۶۴ بیتی را دارند! و برای پردازندهای با سرعت 4GHz این مقدار فقط ۰.۰۰۰۰۰۰۰۰۰۲۵ ثانیه یا یکچهارم نانو ثانیه خواهد بود.
حالا دقت کنید! چرخاندن موتور درایو سخت هزاران نانو ثانیه طول میکشد که تازه دادهها را درون دیسک خود بیابد! چه رسد به انتقال آن. این مسئله در درایوهای جامد دهها یا صدها نانوثانیه طول میکشد.
از طرفی بدیهی است که چنین درایوهایی نمیتوانند در کنار پردازنده ساخته شوند. یعنی بین پردازنده و حافظه ما فاصله فیزیکی وجود خواهد داشت که این فقط زمان بیشتری برای انتقال داده از ما گرفته و اوضاع را بدتر میکند.
بنابر این ما به یک حافظه ذخیره سازی نیازمندیم که واسط پردازنده و حافظه اصلی باشد. این حافظه مورد نیاز ما باید سریعتر از درایو اصلی باشد که بتواند همزمان تعداد زیادی انتقال داده را انجام دهد و به پردازنده نزدیکتر باشد.
درست حدس زدید! ما قبلا همچین چیزی را داریم که رم «RAM» نامیده میشود. و هر رایانهای برای این منظور از آن بهرهمند است.
نام این نوع ذخیره سازی، حافظه دسترسی تصادفی پویا یا Dynamic Random Access Memory است (به اختصار DRAM) که قادر به انتقال سریع دادهها بیش از هر درایو دیگر است.
با اینکه حافظه DRAM بسیار سریعتر است اما توانایی ذخیره سازی حجم زیادی از دادهها را ندارد. برای مقایسه جالب است بدانید برخی از بزرگترین تراشههای حافظه DDR4 ساخته شده توسط Micron، یکی از معدود تولیدکنندگان DRAM، تنها ۳۲ گیگابایت ظرفیت دارد در صورتی که بزرگترین هارد دیسکها ۴۰۰۰ برابر، بیشتر از این مقدار ظرفیت دارند!
بنابر این با اینکه سرعت حافظه را بهبود بخشیدهایم، اما به سیستمهای نرم افزاری و سخت افزاری دیگری نیاز خواهیم داشت تا تعیین کنند چه دادههایی باید در حافظه محدود DRAM برای پردازنده نگهداری شوند.
نکته مثبت این است که در این صورت DRAMها میتوانند به صورت تراشه ساخته شوند که به Embedded DRAM شناخته میشوند. اما به دلیل کوچک بودن پردازندهها، باز هم نمیتوانند درست در کنار آن قرار گیرند.
اکثر DRAMها دقیقا در کنار پردازندهها در مادربرد قرار گرفته و همیشه نزدیکترین جزء به پردازنده در رایانهها هستند. و با این حال هنوز به اندازه کافی سریع نیستند…
هنوز ۱۰۰ نانو ثانیه طول میکشد تا DRAM دادهها را پیدا کند. اما حداقل میتواند میلیاردها بیت را در هر ثانیه منتقل کند. به نظر میرسد ما به یک مرحله دیگر از حافظه نیاز داریم، تا میان پردازنده و DRAM واسطه شود.
قبل از اینکه به راه حل نهایی برسیم بیایید یک مورد دیگر را نیز بررسی کنیم: حافظه دسترسی تصادفی ایستا یا Static Random Access Memory (به اختصار SRAM) که تفاوتهایی با DRAM دارد. DRAM از خازنهای میکروسکوپی برای ذخیره دادهها در قالب بار الکتریکی استفاده میکند. اما SRAM از ترانزیستور برای همینکار استفاده میکند که سرعت را مانند واحدهای منطقی پردازنده میکند. (تقریبا ۱۰ برابر سریعتر از DRAM کار میکند)
حافظههایی که مبتنی بر ترانزیستور هستند (SRAM) فضای بیشتری نسبت به DRAM اشغال میکنند. مثلا به اندازه تراشه 4GB DDR4 شما کمتر از 100MB SRAM دریافت میکنید! اما از آنجایی که فرآیند ساخت SRAM شبیه فرآیند ساخت پردازندهها است، میتوان SRAM را درست داخل پردازنده و نزدیک واحدهای منطقی آن طراحی کرد.
همانطور که شاهد بودید، با قرار دادن واسطه میان واحدهای منطقی پردازنده و حافظه اصلی، توانستیم سرعت را افزایش دهیم و این باعث میشد هر مرحله از ظرفیت حافظه ما کاسته شود؛ اکنون باز هم میتوانیم ظرفیت را کاهش دهیم تا به حافظههایی کوچکتر اما سریعتر دست یابیم.
بسیار خب؛ حالا توانستیم به تعریف فنی و دقیقی از حافظههای پنهان برسیم. این بلوکهای متعدد SRAM است که همه داخل پردازنده قرار گرفته! SRAMها برای اینکه مطمئن شوند واحدهای منطقی پردازنده تا حد ممکن مشغول هستند، با ارسال و ذخیرهسازی بسیار بسیار سریع دادهها مورد استفاده قرار میگیرند. تا اینجا خوب پیش رفت؟ آماده باشید که از این به بعد پیچیدهتر خواهد شد!
کش؛ پارکینگ طبقاتی!
همانطور که گفته شد، ما به حافظه پنهان نیاز داریم. زیرا حافظه جادویی در کار نیست که پاسخگوی نیازهای دادهای واحدهای منطقی پردازندهها باشد. پردازندههای مدرن و پردازندههای گرافیکی (GPU ها) دارای بلوکهای SRAM هستند که از لحاظ درونی به سلسله مراتبی سازماندهی شدهاند.
در تصویر بالا، پردازنده با خط چینهای مشکی نشان داده شده است. ALUها یا همان واحد محاسبه و منطق، در سمت چپ قرار دارند. این ساختارها وظیفه تغذیه و پردازشهای ریاضی را برعهده دارند. اگر دقت کنید، نزدیکترین حافظه ذخیره سازی به واحدهای منطقی پردازنده از نوع حافظه پنهان «Cache» نیستند و این رجیسترها هستند که نزدیکتر اند.
هر یک از واحدهای رجیستر، یک عدد تکی را نگهداری میکند، مثلا یک عدد صحیح ۶۴ بیتی. این مقادیر میتواند هر نوع دادهای باشد. مثلا یک کد برای یک دستورالعمل خاص، یا آدرس حافظه و برخی دادههای دیگر.
فایل رجیستر در پردازندههای دسکتاپ بسیار کوچک است، مانند Core i۹-۹۹۰۰K اینتل که در هر هسته دو فایل رجیستر وجود دارد. بخش مخصوص اعداد صحیح، فقط ۱۸۰ رجیستر ۶۴ بیتی دارد. همچنین رجیستر برای بردارها (آرایههای کوچک از اعداد) نیز کاربرد دارد و دارای ۱۶۸ ورودی ۲۵۶ بیتی است. در نتیجه ظرفیت رجیستر برای هر هسته کمتر از ۷ کیلوبایت خواهد بود. برای مثال ظرفیت فایل رجیستر در سیستمهای چند پردازندهای «Multiprocessors Streaming» مثل پردازنده گرافیکی GeForce RTX ۲۰۸۰ Ti حدود ۲۵۶ کیلوبایت خواهد بود.
رجیسترها درست مانند حافظه پنهان SRAM هستند، ولی بسیار سریع بوده و دادهها را در یک کلاک داخل و خارج میکنند؛ درست مطابق سرعت واحدهای منطقی پردازنده (ALU ها). همانطور که شاهد بودید رجیسترها ظرفیت بالایی نداشته و ما برای ذخیره دادههای بزرگتر همیشه نیازمند حافظهای با ظرفیت بیشتر هستیم. این دلیل وجود حافظهای بزرگتر در همین نزدیکی به نام کش L۱ است.
تصویر بالا نمای بزرگ شده یک هسته از پردازنده دسکتاپ اینتل Skylake است.
در قسمت چپ واحدهای منطقی و محاسباتی پردازنده (ALU ها) قابل مشاهده است. بالای تصویر با رنگ سفید حافظه کش سطح ۱ (L۱) قرار دارد. ظرفیت کش L۱ خیلی بالا نیست، تنها ۳۲ کیلوبایت است. اما مانند رجیسترها به ALUها بسیار نزدیک است و سرعتی مانند آنها دارد.
مستطیل دیگری که در سمت راست تصویر وجود دارد، نشان دهنده حافظه پنهان سطح ۱ است که حجم آن هم ۳۲ کیلوبایت است. همانطور که از نام آن مشخص است (Level ۱ Instruction) دستورات مختلف را برای عملیات کوچکتر (به اصطلاح میکرو عملیات) ذخیره میکند. برای این دستورات هم حافظهای پنهان وجود دارد که میتوان نام آن را کش L۰ در نظر گرفت، زیرا بسیار کوچک بوده و از L۱ به ALUها نزدیکتر است. جالب است بدانید تنها ۱۵۰۰ عملیات را در خود نگهداری میکند.
شاید برایتان سوال شود چرا بلوکهای SRAM اینقدر کوچک هستند؟ چرا ظرفیت آنها به مگابایت نمیرسد؟ باید گفت اندازه حافظههای پنهان با اندازه واحدهای منطقی پردازنده برابر است و در نتیجه افزایش حافظه پنهان باعث افزایش تمام قالب پردازنده میشود.
اما دلیل اصلی اینکه چرا آنها فقط چند کیلوبایت حجم دارند، این است که با افزایش ظرفیت، زمان مورد نیاز برای یافتن و بازیابی دادهها نیز افزایش مییابد. کش L۱ باید بسیار سریع باشد، بنابر این باید بین ظرفیت و سرعت به تعادل برسیم. در بهترین حالت برای دسترسی به دادهها و استخراج آنها به ۵ سیکل کلاک نیاز داریم.
اما اگر این تنها حافظه پنهان در پردازنده بود، عملکرد آن به مشکل برمیخورد. اینجا است که سخن از حافظه سطح ۲ به میان میآید. کش L۲ بلوکی است که از دستورالعملها و دادهها حفاظت میکند.
ظرفیت L۲ همیشه بیشتر از L۱ است. پردازنده AMD Zen ۲ از حافظه پنهان سطح دومی با ظرفیت ۵۱۲ کیلوبایت استفاده میکند! درست است؛ این افزایش حجم، هزینه خود را داشته و در این سطح نیمی از سرعت خود را نسبت به L۱ از دست میدهیم.
بیایید کمی به گذشته بازگردیم، به روزهای اولیه Intel Pentium که حافظه پنهان سطح ۲، خارج از پردازنده بود! در این صورت یا در تراشهای کوچک مانند رم و یا در مادربرد اصلی قرار میگرفت. تا اینکه سرانجام در پردازندههای Pentium III و AMD K6-III با پردازنده ادغام شد. این اتفاق زمانی بود که پردازندههای چند هستهای توسعه پیدا کرد و لازم بود سطوحی دیگر از حافظه پنهان برای پشتیبانی از سایر سطوح پایینتر به پردازنده اضافه شوند.
این تصویر، تراشه چهار هستهای Kaby Lake اینتل است که در مرکز و سمت چپ تصویر هستهها قابل مشاهده بوده و سمت راست تصویر، GPU داخلی قرار دارد. هر هسته دارای بخش اختصاصی کش L۱ و L۲ بوده (قسمت سفید و زرد) و همه هستهها از کش L۳ نیز برخوردار هستند.
حافظه پنهان سطح ۳ حتی اگر اختصاص به یک هسته واحد داشته باشد، کاملا با دیگر هستهها به اشتراک گذاشته میشود و هر یک از هستهها میتوانند به محتویات L۳ هستههای دیگر دسترسی داشته باشد. ظرفیت آن بسیار بیشتر بوده (بین ۲ الی ۳۲ مگابایت) اما بسیار کندتر است. به طور متوسط بیش از ۳۰ سیکل زمان برده به ویژه اگر هستهای به دادههای کش دورتری نیاز داشته باشد.
در زیر میتوانیم یک هسته واحد در معماری پردازنده Zen ۲ AMD ببینیم. حافظههای ذخیره کننده اطلاعات و دستورالعملها که ۳۲ کیلوبایت ظرفیت داشته و در سطح ۱ قرار دارد به رنگ سفید و سطح ۲ با ظرفیت ۵۱۲ کیلوبایت به رنگ زرد و بلوک بزرگ سطح ۳ که ۴ مگابایت ظرفیت دارد، با رنگ قرمز به نمایش درآمده است.
یک لحظه صبر کنید! درست میبینیم؟! چگونه ۳۲ کیلوبایت میتواند فضای بیشتری از ۵۱۲ کیلوبایت اشغال کند؟! اگر دادههای L۱ کمتر است، چرا حجم آن از L۲ یا L۳ اینقدر بیشتر است؟
مفهومی فراتر از عدد
حافظه پنهان با افزایش سرعت انتقال دادهها به واحدهای منطقی و نگهداری از دستورالعملها و دادههایی که مکررا مورد استفاده قرار میگیرند، عملکرد پردازندهها را افزایش میدهد. اطلاعاتی که در حافظههای پنهان ذخیره میشود را میتوان به دو بخش تقسیم کرد؛ خود دادهها و آدرس موقعیت دادهها در حافظه اصلی سیستم. این آدرسها تگ کش نامیده میشود.
زمانی که پردازنده عملیاتی را اجرا میکند، میخواهد دادهها را از حافظه بخواند یا در حافظه دادهای بنویسد. در این حین پردازنده با بررسی برچسبهایی که در کش L۱ وجود دارد میتواند تقریبا بلافاصله به آن دادهها دسترسی پیدا کند. «Cache Miss» زمانی اتفاق میافتد که تگ مورد نیاز در آخرین سطح حافظه پنهان قرار داشته باشد. در این حالت یک تگ جدید در L۱ ایجاد شده و بقیه معماری پردازنده به جستجوی تگ مورد نظر در دیگر سطوح حافظه پنهان میگردد. باید بدانید برخی اوقات ممکن است تگ مورد نظر در دیگر سطوح کش نیز یافت نشود و جستجو در حافظه اصلی ادامه یابد! بنابراین همیشه ممکن است تگها از L۱ به L۲ سرریز شوند.
فرایند مذکور باعث شده در چند کلاک دادهها همواره به صورت تصادفی جا به جا شوند. برای همین امر نیاز به ساختاری پیچیده برای مدیریت SRAM و دادههای موجود در آن را داریم. اگر پردازندهها همواره از یک ALU تشکیل میشدند، L۱ بسیار سادهتر بود، اما اکنون پردازندهها از دهها واحد منطقی (ALU) تشکیل میشوند که کش برای حفظ جریان دادهها نیاز به چندین اتصال دارد.
می توانید از برنامههای رایگان مانند CPU-Z برای بررسی اطلاعات حافظه پنهان پردازنده خود استفاده کنید.
اطلاعات کش بالا مربوط به پردازنده Core i۷-۹۷۰۰K اینتل است. حافظه پنهان سطح ۱ آن هر کدام به ۶۴ بلوک کوچکتر تقسیم میشوند که «Set» نامیده میشوند و هر یک از آنها به بخشهای کوچکتر ۶۴ بایتی به نام «Cache Lines» تقسیم میشوند.
عبارت «8way» در تصویر بالا به ما میگوید هر یک از بلوک داده در دستهها میتواند به ۸ خط کش مرتبط شود. هرچه راهها (Way ها) بیشتر باشد، شانس «Cache Hit» هنگامی که ALU به دنبال دادهها میرود افزایش مییابد. جنبه منفی این اتفاق این است که پیچیدگی افزایش پیدا کرده و مصرف برق افزایش پیدا میکند. همچنین ممکن است از کارایی پردازنده کاسته شود، زیرا خطوط کش بیشتری برای پردازش یک بلوک داده وجود دارد.
علت دیگر این پیچیدگی حافظه پنهان، نحوه نگهداری دادهها در سطوح مختلف است. قوانین این فرآیند «Inclusion Policy» نامیده میشود. مثلا پردازندههای اینتل، ساختار Fully Inclusive L۱+L۳ دارند. این بدان معنا است که همان دادههای سطح ۱، مثلا میتوانند در سطح ۳ نیز باشند. شاید فکرکنید این ساختار باعث میشود ظرفیت کش هدر برود، اما با دقت بیشتر در میابیم که اگر پردازنده نتواند تگی را در لایههای پایین پیدا کند، دیگر نیازی به جستجوی آن در لایههای بالاتر را ندارد.
ساختار عدم کپی شدن دادهها در L۲ باعث صرفهجویی در فضا میشود، اما باعث میشود پردازنده، L۳ که همیشه ظرفیت بیشتری دارد را برای یافتن تگ مد نظر انخاب کند. ساختار دیگری در معماری کش نیز وجود دارد که «Victim Caches» نامیده میشود. این نوع از کش برای ذخیره دادههای سرریز شده از سطوح پایینتر استفاده میشود. مثلا پردازندههای Zen ۲ AMD از L۳ Victim Cache استفاده میکنند که فقط دادههای کش L۲ را ذخیره سازی میکند.
سیاست دیگری نیز در مورد حافظه پنهان وجود دارد که قابل توجه است، زمان مشخص شدن ذخیره دادهها در حافظه پنهان و حافظه اصلی سیستم است. این ساختار، Write Policies نام دارد که اکثر پردازندههای امروزی از سیاست Write-Back بهره میگیرند. این یعنی وقتی دادهای روی سطحی از کش نوشته میشود، با مقداری تاخیر در حافظه اصلی کپی از آن داده بهروز خواهد شد. در بیشتر موارد ارسال داده متوقف بر زمانی است که داده دیگر در کش موجود نباشد، و آن زمان است که حافظه رم آن داده را دریافت میکند.
طراحان پردازنده، برای رسیدن به تعادل، تصمیمگیریهای مختلفی از جمله در مورد مقدار، نوع و سیاست حافظه پنهان، ظرفیت و پیچیدگی بیشتر و اندازه حجم قالب پردازنده انجام میدهند. اگر ممکن بود حافظه پنهان سطح ۱ «1000Way» را با ظرفیت ۲۰ مگابایت در کنار پردازنده داشت، هم اکنون همه ما رایانههایی با چنین تراشههایی داشتیم!
پایینترین سطح حافظه نهان در پردازندههای امروزی نسبت به یک دهه گذشته تغییر چندانی نکرده است، اما حافظه سطح ۳ همچنان در حال رشد است. یک دهه پیش ممکن بود با ۹۹۹ دلار صاحب i۷-۹۸۰X اینتل با ۱۲ مگابایت کش L۳ شد، اما امروزه میتوان با نصف این قیمت، پردازندهای با ۶۴ مگابایت کش L۳ صاحب شد.