Швидкодія динамічної оперативної пам'яті і безглузда ідея як її збільшити

Трохи історії

На зорі обчислювальної техніки динамічна пам'ять цілком собі працювала на частоті процесора. Мій перший досвід роботи з комп'ютером був пов'язаний з клоном комп'ютера «ZX Spectrum». Процесор Z80 здійснював обробку інструкцій в середньому по 4 такти на операцію, при цьому два такту використовувалося на здійснення регенерації динамічної пам'яті, що дає нам при частоті в 3,5 МГц, не більше 875 000 операцій в секунду.

Однак через деякий час частоти процесорів досягли такого рівня, коли динамічна пам'ять вже не справлялася з навантаженням. Для компенсації цього, було введено проміжну ланку у вигляді кеш-пам'яті, що дозволило за рахунок операцій виконуваних на невеликому обсязі даних згладити різницю в швидкості роботи процесора і основної пам'яті.

Давайте розглянемо що вдає із себе оперативна пам'ять комп'ютера зараз, і що з нею можна зробити, щоб збільшити швидкодію комп'ютерної системи.

Коротенько про статичної та динамічної пам'яті

Осередок статичної пам'яті будується на основі тригера, який зазвичай знаходитися в одному зі стабільних станів «А» або «Б» (А =! Б). Мінімальна кількість транзисторів для одного осередку становить 6 штук, при цьому складність трасування в осередках мабуть не дозволяє зробити модулі статичної пам'яті в 1 гіг, за ціною звичайного модуля в 8 гіг.

Осередок динамічної пам'яті складається з одного конденсатора відповідає за зберігання інформації і одного транзистора відповідає за ізоляцію конденсатора від шини даних. При цьому в якості конденсатора використовується не навісний електроліт, а паразитная ємність p-n переходу між «підкладкою» і електродом транзистора (спеціально для цих цілей збільшена, зазвичай від неї намагаються позбутися). Недоліком конденсатора є струм витоку (як в ньому самому, так і в ключовому транзисторі) від якого дуже складно позбутися, крім того з збільшенням температури він збільшується наслідком чого стане ймовірність спотворення інформації, що зберігається. Для підтримки достовірності, в динамічної пам'яті застосовується «регенерація», вона полягає в періодичному оновленні інформації, що зберігається не менше заданого періоду протягом якого інформація зберігає достовірне значення. Типовий період регенерації становить 8 мс, при цьому частіше оновлювати інформацію можна, рідше не рекомендується.

В іншому принцип функціонування ідентичний і полягає в наступному:

- первісна вибірка рядки пам'яті призводить до доступу до всього її вмісту помещаемому в буферну рядок з якої йде подальша робота, або відбувається мультиплексування звернення до стовпців (старий, повільний підхід);
- запитані дані передаються до головного пристрою (зазвичай це ЦПУ), або відбувається модифікація заданих осередків при операції запису (тут є невелика різниця, для статичної пам'яті можлива безпосередня модифікація осередку вибраного рядка, для динамічної пам'яті модифікується буферна рядок, і тільки потім виконується зворотна запис вмісту всього рядка в спеціальному циклі);
- закриття і зміна рядка пам'яті так-же різна для різного типу пам'яті, для статичної можлива миттєва зміна рядка якщо дані не змінювалися, для динамічної пам'яті необхідно вміст буферної рядки обов'язково записати на місце, і тільки потім можна вибрати інший рядок.

Якщо на зорі обчислювальної техніки кожна операція читання або запису завершувалася повним циклом пам'яті:

- вибір рядка;
- операція читання / запису з осередку;
- зміна / перевибір рядки.

Сучасний операції роботи з мікросхемами «синхронної пам'яті а ля DDRX» полягає в наступному:

- вибір рядка;
- операції читання / запису осередків рядка групами по 4-8 біт / слів (допускається множинне звернення в рамках одного рядка);
- закриття рядка із записом інформації на місце;
- зміна / перевибір рядки.

Таке рішення дозволило заощадити час доступу до даних коли після читання значення з комірки «1», потрібно звернення до осередків «2, 3, 4, або 7» розташованим в тій-же рядку, або відразу після операції читання, необхідно записати назад змінене значення .

Потрібно обов'язково пам'ятати що у оперативної пам'яті DDR є дві частоти:

- основна тактова частота визначає темп передачі команд і таймінги;
- ефективна частота передачі даних (подвоєна тактова частота, якій і маркуються модулі пам'яті).

Інтеграція контролера пам'яті збільшило швидкодії підсистеми пам'яті за рахунок відмови від проміжного передавального ланки. Збільшення каналів пам'яті требудет враховувати це з боку додатка, так наприклад чотирьох канальний режим при певному розташуванні файлів не дає приросту продуктивності (12 і 14 конфігурації).


Обробка одного елемента зв'язного списку з різним кроком (1 крок = 16 байт)

Тепер трохи математики

Процесор: робочі частоти процесорів зараз досягають 5 ГГц. За заявами виробників, схемотехнічні рішення (конвеєри, передбачення та інші хитрощі) дозволяють виконувати одну інструкцію за такт. Для округлення розрахунків візьмемо значення тактової частоти в 4 ГГц що дасть нам одну операцію за 0,25 нс.

Оперативна пам'ять: візьмемо для прикладу оперативну пам'ять нового формату DDR4-2133 з таймингом 15-15-15.

процесор
Fтакт = 4 ГГц
Tтакт = 0,25 нс (за сумісництвом час виконання однієї операції «умовно»)

Оперативна пам'ять DDR4-2133
Fтакт = 1066 МГц
Fдата = 2133 МГц
tтакт = 0,94 нс
tдата = 0,47 нс
СПДмакс = 2133 МГц * 64 = 17064 Мбайт / с (швидкість передачі даних)
tRCmin = 50 нс (мінімальний час між двома активації рядків)

Час отримання даних

З регістрів і кеша, дані можуть бути надані протягом робочого такту (регістри, кеш 1 рівня) або із затримкою в кілька тактів процесора для кеша 2-го і 3-го рівня.

Для оперативної пам'яті ситуація гірша:

- час вибору рядка становить: 15 clk * 0,94 нс = 14 нс
- час до отримання даних з команди вибору стовпця: 15 clk * 0,94 нс = 14 нс
- час закриття рядка: 15 clk * 0,94 нс = 14 нс (хто б подумав)

З чого випливає що час між командою запитуючої дані з комірки пам'яті (у разі якщо в кеш не були) може варіюватися:

14 нс - дані знаходяться в уже вибраному рядку;
28 нс - дані знаходяться в невибраною рядку за умови що попередній рядок вже закрита (блок в стані «idle»);
42-50 нс - дані знаходяться в іншому рядку, при цьому поточна рядок потребує закриття.

Кількість операцій які може виконати (вищезазначений) процесор за цей час становить від 56 (14 нс) до 200 (50 нс зміна рядка). Окремо варто відзначити що на часі між командою вибору стовпця і отриманням всього пакета даних додається затримка завантаження рядки кешу: 8 біт пакета * 0,47 нс = 3,76 нс. Для ситуації коли дані будуть доступні «програмою» тільки після завантаження рядки кешу (хто знає що і як там накрутили розробники процесорів, пам'ять по специфікації дозволяє видати потрібні дані вперед), ми отримуємо ще до 15-ї пропущених тактів.

В рамках однієї роботи я проводив дослідження швидкості роботи пам'яті, отримані результати показали, що повністю «утилізувати» пропускну здатність пам'яті можливо тільки в операціях послідовного звернення до пам'яті, в разі довільного доступу збільшується час обробки (на прикладі зв'язного списку з 32-х бітного покажчика і трьох подвійних слів одне з яких оновлюється) з 4-10 (послідовний доступ) до 60-120 нс (зміна рядків) що дає різницю в швидкості обробки в 12-15 разів.

Швидкість обробки даних

Для обраного модуля маємо пікову пропускну здатність в 17064 Мбайт / с. Що для частоти в 4 ГГц дає можливість обробляти за такт 32-х бітні слова (17064 Мб / 4000 МГц = 4,266 байт на такт). Тут накладаються наступні обмеження:

- без явного планування завантаження кешу, процесор буде змушений простоювати (чим вище частота, тим більше ядро ​​просто чекає дані);
- в циклах «читання модифікація запис» швидкість обробки знижується в два рази;
- багатоядерні процесори розділять між ядрами пропускну здатність шини пам'яті, а для ситуації коли будуть конкуруючі запити (вироджених випадок), продуктивність роботи пам'яті може погіршитися в «200 раз (зміна рядків) * Х ядер».

17064 Мбайт / с / 8 ядер = 2133 Мбайт / с на ядро ​​в оптимальному випадку.
17064 Мбайт / с / (8 ядер * 200 пропущених операцій) = 10 Мбайт / с на ядро ​​для виродженого випадку.

У перекладі на операції отримуємо для 8-й ядерного процесора: від 15 до 400 операцій на обробку байта даних, або від 60 до 1600 операцій / тактів на обробку 32-х бітного слова.

На мій погляд повільно якось. У порівнянні з пам'яттю DDR3-1333 9-9-9, де час повного циклу приблизно дорівнює 50 нс, але відрізняються час таймингов:

- час доступу до даних зменшується до 13,5 нс (1,5 нс * 9 тактів);
- час передачі пакета з восьми слів 6 нс (0,75 * 8 замість 3.75 нс) і при випадковому доступі до пам'яті, різниця в швидкості передачі даних практично зникає;
- пікова швидкість складе 10 664 МБайт / с.

Чи не занадто все далеко пішло. Ситуацію трохи рятує наявність в модулях пам'яті «банків». Кожен «банк» є окремою таблицю пам'яті до якої можна звертатися окремо, що дає можливість змінити рядок в одному банку поки йде читання / запис даних з рядка іншого, за рахунок зменшення простою дозволяє «забити» шину обміну даними під зав'язку в оптимізованих ситуаціях.

Власне тут пішли безглузді ідеї

Таблиця пам'яті, містить в собі задану кількість стовпців, рівний 512, 1024, 2048 біт. З урахуванням часу циклу по активації рядків в 50 нс, ми отримуємо потенційну швидкість обміну даними: «1 / 0,00000005 з * 512 стовпців * 64 біт слово = 81 920 Мбайт / с» замість поточних 17 064 Мбайт / с (163 840 і 327 680 МБайт / с для рядків з 1024 і 2048 стовпців). Скажете: «всього раз в 5 (4,8) швидше», на що я відповім: «це швидкість обміну, коли всі конкуруючі запити звернені до одного банку пам'яті, і доступна пропускна можливість збільшується пропорційно кількості банків, і збільшенням довжини рядка кожної таблиці (зажадає збільшення довжини операційної рядки), що в свою чергу впирається головним чином в швидкість шини обміну даними ».

Зміна режиму обміну даними зажадає передачі всього вмісту рядка в кеш нижнього рівня, для чого треба розділити рівні кеша не тільки по швидкості роботи, але і за розміром кеш рядка. Так наприклад реалізувавши «довжину» рядки кешу N-го рівня в (512 стовпців * 64 розмір слова) 32 768 біт, ми можемо за рахунок зменшення кількості операцій порівняння збільшити загальну кількість рядків кеша і відповідно збільшити максимальний його обсяг. Але якщо зробити паралельну шину в кеші такого розміру, ми можемо отримати зменшення частоти функціонування, з чого можна застосувати інший підхід організації кеша, якщо розбити зазначену «Jumbo» -строку кеша на блоки по довжині рядка верхнього кеша і проводити обмін з невеликими порціями, це дозволить зберегти частоту функціонування, розділивши затримку доступу на етапи: пошук рядка кеша, і вибірку потрібного «слова», в знайденої рядку.

Що стосується безпосередньо обміну між кешем і основною пам'яттю: необхідно передавати дані з темпом звернення до рядків одного банку, або маючи певний запас для розподілу запитів до різних банках. Крім цього, є складність з часом доступу до даних розміщених в різних областях рядки, для послідовної передачі крім первісної затримки пов'язаної з вибіркою рядки, є затримка передачі даних залежить від кількості даних «в пакеті», і швидкості передачі. Навіть підхід «rambus» може не впоратися із збільшеним навантаженням. Ситуацію може врятувати перехід на послідовну шину (можливо диференціальну), за рахунок подальшого зменшення розрядності даних, ми можемо збільшити пропускну швидкість каналу, я для зменшення часу між передачею першого і останнього біта даних, застосувати поділ передачі рядка на кілька каналів. Що дозволить використовувати меншу тактову частоту одного каналу.

Оцінимо швидкість такого каналу:

1 / 0,00000005 нс = 20 МГц (частота зміни рядків в рамках одного блоку)
20 Мгц * 32 768 біт = 655 360 Мбіт / с
Для диференціальної передачі з тим-же розміром шини даних отримуємо:
655 360 Мбіт / с / 32 каналу = 20 480 Мбіт / с на канал.

Така швидкість виглядає прийнятно для електричного сигналу (10 Гбіт / с для сигналу з вбудованою синхронізацією на 15 метрів доступний, чому б і 20 Гбіт / с з зовнішньою синхронізацією на 1 метр не осилити), однак необхідне подальше збільшення швидкості передачі для зменшення затримки передачі між першим і останнім бітом інформації, може вимагати збільшення пропускної здатності, з можливою інтеграцією оптичного каналу передачі, але це вже питання до схемотехніки, у мене обмаль досвіду роботи з такими частотами.

і тут Остапа понесло

Зміна концепції проектування кешу на основну пам'ять до використання «основний пам'яті як проміжного надшвидкодіючих блочного накопичувача» дозволить перекласти пророкування завантаження даних з схемотехніки контролера на алгоритм обробки (а вже кому краще знати куди він ломанется через деякий час, явно не контролеру пам'яті), що в свою чергу дозволить збільшити обсяг кеша зовнішнього рівня, без шкоди продуктивності.

Якщо піти далі можна додатково змінити концепцію орієнтування архітектури процесора з «перемикання контексту виконавчого пристрою», на «робоче оточення програми». Така зміна може суттєво поліпшити безпеку коду через визначення програми як набору функцій з заданими точками входу окремих процедур, доступним регіоном розміщення даних для обробки, і можливістю апаратного контролю можливості виклику тієї чи іншої функції з інших процесів. Така зміна дозволить також ефективніше використовувати багатоядерні процесори за рахунок позбавлення від перемикання контексту для частини потоків, а для обробки подій використовувати окремий потік в рамках доступного оточення «процесу», що дозволить ефективніше використовувати 100+ ядерні системи.

P.S. випадкове використання зареєстрованих товарних знаків або патентів є випадковим. Всі оригінальні ідеї доступні для використання за ліцензійною угодою «мурашник».