Королевство кошмаров
Память.. Ее всегда мало. И мы всегда будем чувствовать нехватку оперативной памяти, даже если она увеличится в 10 раз относительно сегодняшних технологических достижений. К примеру, имея на борту видеокарты один гигабайт памяти, мы думали - ого дак это очень много - в один гигабайт влезет за одну загрузку все текстуры игры “Quake 2“ и еще останется куча места. Но сейчас - три гигабайта никого не удивят. Текстуры стали большими, сложные процедуры генерации полигонов, стали выделять намного больше памяти, например, для отображения очень реалистичного взрыва со всеми частицами. А когда речь заходит об нейронных сетях и вычислениях на GPU, то порой и трех гигабайт будет мало, чтоб умножить огромные матрицы (трехмерные матрицы) - имеются ввиду операции свертки.
Но как же современные видеокарты работают с памятью? В этой главе я постараюсь ответить на этот вопрос. Будет большим плюсом знание страничной организации памяти в процессорах Intel. Не поленитесь вникнуть. Это облегчит понимание данной главы. Что ж, приступим.
Рис 1.
На рисунке 1 представлена схема отображения между физической памятью в пространстве PCI и видеопамятью внутри видеокарты. Из этой схемы можно сделать вывод, что по факту мы можем адресовать через шину только 256 мегабайт. Это та область памяти которая доступна для процессора и видеокарты одновременно. Еще эту память называют Visible Framebuffer. Все, что выше этой памяти - называется Invisible Framebuffer (потому, что он невидим для центрального процессора). Один гигабайт памяти видеокарты взят как пример и не стоит заострять на этом внимание. То что нас может интересовать - это GART. Расшифровывается как - Graphics Address Remapping Table. То есть, таблица трансляции адресов графической памяти. Современные видеокарты позволяют адресовать память 36 битами, что дает возможность адресации 64 гигабайта памяти. Но даже имея такую невероятную цифру мы все равно не можем обойтись без таблиц трансляции и механизма прямого доступа к памяти устройства (DMA).. Ведь напрямую через центральный процессор доступно всего лишь 256 мегабайт. А остальная память нам недоступна. Таблицы позволяют адресоваться ко всему остальному недоступному для CPU пространству видеопамяти.
Давайте для начала разберем хардварный механизм работы адресации.
Рис 2.
На рисунке 2 два представлен механизм обработки запросов чтения\записи данных. В обработке участвуют несколько блоков. Шина PCIe передает транзакцию в модуль BIF (Bus InterFace). То есть PCIe транзакции идут через унифицированный интерфейс шины BIF. Он управляет всеми запросами которые идут на видеокарту. Это могут быть запросы чтения и записи, запросы идентификации производителя, запросы прерываний, запросы статуса связи, запросы управления питанием, имеет свой генератор тактов. В общем практически все, что связано с железом - проходит через этот модуль. Он работает как шлюз. Также BIF имеет специальный интерфейс модуля HDP (Host Data Patch) для чтения и записи буфера кадров. HDP - модуль настройки адресации. Имеет регистры настройки кэша, регистры настройки тайлинга (Tilling, к этому слову мы вернемся позже). Этот модуль обрабатывает информацию, полученную с BIF и преобразует ее в адрес на GPU, который в свою очередь обрабатывается контроллером памяти GPU MC (Memory Controller). Интерфейс HDP в случае системы с несколькими видеокартами, позволяет организовывать прямой ввод вывод, между ними, минуя создание специального кадра на CPU. У каждого GPU своя апертура (отображение части пространства адресов). Размер апертуры обычно занимает от 32 мегабайт памяти и больше.
Из этого рисунка видно, что в контроллер памяти могут приходить запросы и от CP (Command Processor) и от DRMDMA (Direct Memory Access Engine). Оба этих блока исполняют команды. CP блок мы рассмотрим в другой главе, здесь его роль только в том, что он может получать данные. Что касается блока DRMDMA - его мы рассмотрим чуть подробнее. Этот блок отвечает за перемещение данных между устройствами, так и внутри устройства. Блок DRMDMA - это считайте процессор. Определенные команды, специально подготовленные, обрабатываются и блок, исполняя их, производит операции с памятью. Оговорюсь сразу, что локальная память - это память в видеокарте. А память хоста - это память на CPU. Поддерживаются следующие операции.
- Копирование данных Host Src-> Local Dest. 16 байт за такт
- Операция заполнения локальной памяти константным значением. 32 байта за такт
- Копирование данных Local Dest -> Host Src. 16 байт за такт
- Копирование локальной памяти на удаленный GPU. 32 байта за такт на ближайший GPU, и по 16 байт за такт на GPU находящиеся дальше ближайшего
Эти все названия, кажутся жуткими и хардварными, но важны для понимания архитектуры видеосистемы. Мы должны понимать хотя бы в общих чертах - как идет запрос чтения\записи на видеокарту. Я бы мог расписать все еще подробнее, рассказать про такой важный момент как кеширование, какие используются кеши на видеоконтроллере, и какие биты кешей возводятся и за что они отвечают. Мог бы рассказать про PCIe и как оно организовано, про ACPI. Но чтобы их все охватить - мне пришлось бы уйти далеко от темы GPU. По этому ваш мозг может чуть расслабиться. Для любителей хардкора - можете начать с этой ссылки (по кэшам), погуглить такие магические слова как VIPT и PIPT, далее почитать про PCIe тут. А так же почитать про ACPI тут. Но вернемся к видеопамяти.
Рассмотрим простой пример, как может транслироваться адрес памяти в видеокарте.
Рисунок 3
И так, дан адрес 0x30000000. Как он будет преобразован.
0х30000000 - 0х10000000 = 0х20000000 откидываем 3 знака, получаем 0х20000 - это наш индекс в таблице PTE. Из PTE элемента получаем базовый адрес. Сама таблица находится в видимой части FB (cpu mapped). Регистр базы PT_BASE указывает на адрес этой таблицы. В итоге, тот адрес, указанный в элементе таблицы, и будет искомым физическим адресом в памяти GPU.
Для DMA механизма это все прозрачно. Мы указываем ему адрес из CPU RAM (виртуальный) и адрес GPU RAM (виртуальный). Далее задаем тип операции. Блок DRMDMA сам разберется, куда копировать данные.
Однако при попытке записать в память гпу напрямую, мы увидим, что пиксели закрашиваются не по-порядку, а как будто шахматами. Это так называемый тайлинг (Tilling). Это способ организации (адресации) памяти, при котором байты расположены нелинейно. Образуется некоторая сетка. Это сделано с целью оптимизировать доступ к пикселям. Так как кеш у видеокарты ассоциативный, и загружается сразу порция данных, то доступ к памяти происходит быстрее когда идет такая несимметричнная и нелинейная запись. Меньше кеш коллизий, меньше кеш промахов. Ниже схематично представлен тайлинг:
Осталось рассмотреть еще одну очень важную деталь как дисплей контроллер. Он еще часто называется DAL - Display Abstract Layer. Это контроллер также находится в видеокарте и отвечает за работу с дисплеем. Он напрямую работает с регистрами видеокарты, для настройки таймингов и смены поверхностей. Отвечает за настройку дисплеев, умеет считывать EDID (идентификатор) дисплея, умеет работать с GPIO, I2C и много чего еще. Например когда идет смена кадров, то чтоб отобразить новый кадр на дисплее, приходит прерывание и по нему происходит так называемый Flip. Это механизм смены уже подготовленных областей памяти. Когда у нас обычный режим отображения (не 3д режимы), то контроллер настроен на два адреса - front \ back буферы. При каждой операции Flip - идет запись нового адреса в регистр отвечающий за адрес отображения. Далее, после смены адреса, контроллер устанавливает специальный флаговый регистр, который как раз и инициирует показ картинки дисплеем.
Я думаю, на этом очень интересном месте, мы закончим эту главу. А вывод такой - адресация теперь уже не линейная, и мы не можем как раньше, во времена DOS менять содержимое памяти . записывая туда байт за байтом, и получая красивую картинку.
PS. глава была готова еще 4 года назад, просто все думал ее расширить и дополнить, но сейчас урезал ее до более удобочитаемого вида, и запостил. Времени нет вдаваться в детали и эксперименты с памятью, хотя были идеи показать примеры в WinDBG
© TermoSINteZ
Некрономикон GPU. Глава вторая
Дата публикации 21 фев 2023