Перш за все, доступ до основної пам'яті дуже дорогий. В даний час процесор 2 ГГц (найповільніший один раз) має 2G тиків (циклів) в секунду. ЦП (віртуальне ядро сьогодні) може отримати значення з його регістрів один раз за галочку. Оскільки віртуальне ядро складається з декількох одиниць обробки (ALU - арифметична логічна одиниця, FPU тощо), воно може фактично обробляти певні інструкції, якщо це можливо.
Доступ до основної пам'яті коштує приблизно від 70 до 100с (DDR4 трохи швидше). Цей час, в основному, шукає кеш L1, L2 і L3 і, ніж натискає на пам'ять (відправляємо команду на контролер пам'яті, який надсилає її в банки пам'яті), чекаємо відповіді і робимо.
100ns означає близько 200 кліщів. Отже, якщо програма завжди пропускатиме кеші, до яких має доступ кожна пам'ять, процесор витрачає приблизно 99,5% свого часу (якщо вона лише зчитує пам'ять) в режимі очікування пам'яті.
Для прискорення роботи є кеші L1, L2, L3. Вони використовують пам'ять, безпосередньо розміщуючись на мікросхемі та використовуючи транзисторні схеми іншого типу для зберігання заданих бітів. Це займає більше місця, більше енергії та коштує дорожче, ніж основна пам'ять, оскільки процесор зазвичай виробляється за допомогою більш досконалої технології, а виробничі збої в пам'яті L1, L2, L3 мають шанс зробити процесор нічим не потрібним (дефект). великий кеш L1, L2, L3 збільшує коефіцієнт помилок, що зменшує вихід, що безпосередньо зменшує рентабельність інвестицій. Тож існує велика торгівля, коли мова йде про доступний розмір кешу.
(в даний час один створює більше кешів L1, L2, L3, щоб можна було деактивувати певні частини, щоб зменшити ймовірність того, що фактичний дефект виробництва - це область кеш-пам'яті, видає дефект ЦП в цілому).
Дати уявлення про терміни (джерело: витрати на доступ до кешів та пам'яті )
- Кеш L1: від 1 до 2 с (2-4 цикли)
- Кеш L2: 3ns до 5ns (6-10 циклів)
- Кеш L3: від 12 до 20 секунд (24-40 циклів)
- ОЗУ: 60 секунд (120 циклів)
Оскільки ми змішуємо різні типи процесора, це лише оцінки, але даємо гарне уявлення про те, що відбувається насправді при отриманні значення пам'яті, і ми можемо мати хіт або промах у певному шарі кешу.
Таким чином, кеш в основному значно прискорює доступ до пам'яті (60ns проти 1ns).
Отримання значення, зберігання його в кеші для шансу повторного читання, це добре для змінних, до яких часто звертаються, але для операцій з копіюванням в пам'ять це все одно буде повільним, оскільки один лише читає значення, записує значення десь і ніколи не читає значення знову ж таки ... жодного хіта кеша, мертвий повільний (поряд з цим може траплятися паралельно, оскільки у нас немає виконання порядку).
Ця копія пам’яті настільки важлива, що для її прискорення існують різні засоби. У перші дні пам'ять часто могла копіювати пам'ять поза процесором. Цим керував контролер пам'яті безпосередньо, тому операція копіювання пам'яті не забруднювала кеші.
Але крім звичайної копії пам’яті, звичайний послідовний доступ до пам'яті був досить поширеним. Прикладом є аналіз ряду інформації. Маючи масив цілих чисел і обчислюючи суму, середнє, середнє або навіть більш просте знаходження певного значення (фільтр / пошук), було ще одним дуже важливим класом алгоритмів, що працюють кожного разу на будь-якому процесорі загального призначення.
Отже, аналізуючи схему доступу до пам'яті, було очевидно, що дані читаються послідовно дуже часто. Існувала велика ймовірність того, що якщо програма прочитає значення в індексі i, програма також прочитає значення i + 1. Ця ймовірність трохи вище, ніж ймовірність того, що та сама програма також буде читати значення i + 2 тощо.
Отже, враховуючи адресу пам'яті, це було (і досі є) хорошою ідеєю читати заздалегідь та отримувати додаткові значення. Це причина, коли існує режим підвищення.
Доступ до пам'яті в режимі підвищення означає, що адреса надсилається, і кілька значень послідовно надсилаються. Кожне надсилання додаткового значення займає лише додаткові 10нс (або навіть нижче).
Ще однією проблемою була адреса. Надсилання адреси вимагає часу. Щоб адресувати велику частину пам'яті, потрібно надсилати великі адреси. У перші дні це означало, що адресна шина не є достатньо великою, щоб надсилати адресу в одному циклі (галочка), а для відправлення адреси потрібно було більше одного циклу, додаючи більше затримки.
Рядок кеш-пам'яті в 64 байти, наприклад, означає, що пам'ять розділена на окремі (не перекриваються) блоки пам'яті розміром 64 байт. 64 байти означають, що стартова адреса кожного блоку має найнижчі шість бітів адреси, які завжди будуть нулями. Тому щоразу надсилати ці шість нульових бітів не потрібно, збільшуючи адресний простір у 64 рази на будь-яку кількість ширини адресної шини (ефект привітання).
Ще одна проблема, яку вирішує кеш-лінія (окрім читання вперед та збереження / звільнення шести біт на адресній шині), полягає в тому, як організовано кеш-пам'ять. Наприклад, якщо кеш буде розділений на 8-байтних (64-бітових) блоків (комірок), потрібно зберегти адресу комірки пам’яті, для якої ця клітинка кешу містить разом із нею значення. Якщо адреса також буде 64-бітовою, це означає, що половина розміру кешу споживається адресою, що призводить до накладних витрат у 100%.
Оскільки лінія кешу становить 64 байт і процесор може використовувати 64 біт - 6 біт = 58 біт (не потрібно зберігати нульові біти занадто правильно), то ми можемо кешувати 64 байт або 512 біт з накладними витратами на 58 біт (11% накладних витрат). Насправді адреси, що зберігаються, навіть менші, ніж ці, але є інформація про стан (наприклад, правильність та точність кеш-пам’яті, брудна та її потрібно записати в операційному режимі тощо).
Ще один аспект полягає в тому, що ми маємо набір-асоціативний кеш. Не кожна клітинка кешу здатна зберігати певну адресу, а лише підмножину таких. Це робить необхідні біти збереженої адреси ще меншими, дозволяє паралельний доступ до кешу (до кожного підмножини можна отримати доступ один раз, але незалежно від інших підмножин).
Тим більше, що стосується синхронізації доступу до кеш-пам'яті / пам’яті між різними віртуальними ядрами, їх незалежними декількома одиницями обробки на одне ядро та, нарешті, декількома процесорами на одній материнській платі (в яких є плати, що містять до 48 процесорів і більше).
Це в основному поточна ідея, чому у нас є кешові рядки. Користь від читання вперед дуже велика, і найгірший випадок зчитування одного байта з кеш-лінії та ніколи не читати решту знову дуже невеликий, оскільки ймовірність дуже мала.
Розмір кеш-лінії (64) є розумним обраним компромісом між більшими лініями кешу, що малоймовірно, щоб останній байт його також був прочитаний найближчим часом, тривалість, необхідна для отримання повної лінії кешу з пам'яті (і записати її назад), а також накладні витрати в організації кешу і паралелізація кешу та доступу до пам'яті.