А что если бы видеопамять можно было использовать как обычную RAM?

от автора

С чего всё началось

У меня 8 гигабайт оперативки. На бумаге звучит терпимо — ровно до того момента, пока не откроешь десятка два вкладок в хроме, рядом Figma, Slack, ещё вкладку со Stack Overflow, и поверх всего этого попробуешь что-то писать в VS Code. Сначала система начинает подтормаживать. Потом подтормаживать сильнее. В какой-то момент я просто встаю и иду за чаем — и, возвращаясь, нередко застаю её всё в той же задумчивости.

Логичное решение — докупить плашку. Но тут засада: один слот на плате занят, а второго… просто нет. Ноут. (Хотя, если честно, будь даже свободный слот — не факт, что прямо сейчас нашлись бы деньги на лишнюю плашку.)

И вот сижу я, смотрю в диспетчер задач: RAM забита под потолок, а видеокарта со своими 6 ГБ VRAM стоит в сторонке и в ус не дует. Никакой тяжёлой графики, просто простаивает.

Тут-то вопрос и нарисовался: а нельзя ли, хотя бы теоретически, приспособить видеопамять под расширение оперативки?

Почему «вместо RAM» — сразу не то слово

Оговорюсь заранее, иначе в комментариях прилетит «автор не понимает, о чём пишет».

К обычной RAM процессор обращается напрямую — через контроллер памяти, который сидит прямо на кристалле. Задержка — где-то 70 наносекунд. Это очень быстро.

VRAM физически живёт на видеокарте, по ту сторону шины PCIe. Чтобы дотянуться до байта в видеопамяти, процессору нужно собрать транзакцию, прогнать её через слот, дождаться, пока GPU ответит, и забрать данные назад. Это уже микросекунды, то есть на пару порядков медленнее.

Плюс туда не положишь таблицы страниц, и инструкции оттуда процессор с нормальной скоростью декодировать не умеет. Так что «использовать VRAM как RAM» в прямом смысле — нет, не выйдет.

Но рядом есть другая идея, и вот она вполне живая: сделать из VRAM очень быстрый своп.

Своп — штука, которая не раз спасала мне работу

Когда оперативка кончается, ОС не падает замертво. Она берёт страницы, к которым давно никто не обращался, и спихивает их куда-нибудь на диск. Это и есть своп, он же файл подкачки.

Обычно своп лежит на диске — HDD или SSD. Медленнее RAM, но несравнимо лучше, чем поймать out of memory и потерять несохранённое (привет всем, кто хоть раз так ронял полдня работы).

А что, если положить своп не на диск, а в VRAM?

Видеопамять — это, конечно, не DDR4. Но на последовательных операциях она ощутимо быстрее даже приличного NVMe. Очень грубо, по порядку величин:

Носитель

Последовательное чтение

Задержка

DDR4 RAM

~22 000 МБ/с

~70 нс

VRAM-своп (теоретически)

~3 800 МБ/с

~12 мкс

Хороший NVMe

~2 400 МБ/с

~25 мкс

SATA SSD

~520 МБ/с

~120 мкс

Цифры тут заведомо прикидочные — надёргала из datasheet’ов и собственных старых замеров, так что относитесь к ним как к «плюс-минус», а не как к честному бенчмарку. Но даже так выходит, что VRAM-своп мог бы оказаться раз в 5–10 быстрее дискового. Для фоновых вкладок хрома, которые висят и просыпаются раз в полчаса, этого за глаза.

Как это можно было бы собрать

Вот тут начинается самое любопытное. ОС про VRAM ничего не знает — она умеет в блочные устройства: диски, флешки, разделы. Значит, нужно как-то прикинуться, что кусок видеопамяти — это блочное устройство.

Один из красивых вариантов — NBD. Протокол, в котором userspace-программа ловит команды «прочитай блок», «запиши блок», а внутри творит что хочет. Linux держит NBD из коробки, под Windows есть аналог — WNBD.

Картинка примерно такая:

Браузер, Figma, VS Code...        ↕ (страницы памяти)    Ядро ОС    (своп / файл подкачки)        ↕ (блочные операции)   NBD / WNBD драйвер        ↕ (TCP loopback) Наш гипотетический сервер   (читает/пишет в VRAM через OpenCL)        ↕ (PCIe)          GPU

OpenCL тут — кросс-платформенный API к GPU: позволяет выделять буферы прямо в видеопамяти и читать-писать в них с хоста. Живёт на NVIDIA, AMD и Intel.

TCP loopback поначалу смущает — вроде лишний оверхед. Но на запросах от 4 КБ и выше он почти растворяется на фоне самой передачи по PCIe. Нормальный размен на простоту реализации.

Грабли, на которые стоит посмотреть заранее

Если вдруг кто-то решит это и правда собрать — есть несколько неочевидных мест.

VRAM волатильна, и в очень неприятном смысле. Обычная RAM теряет данные, только когда совсем выдёргиваешь питание. VRAM же может обнулиться при ресете драйвера (тот самый TDR — драйвер решил, что GPU завис, и передёрнул его), при подключении или отключении монитора, при уходе в сон. Для свопа это, в общем, переживаемо — он и так волатилен, ОС перечитает данные с диска. Но обычный дисковый своп держать про запас обязательно, иначе один сбой — и kernel panic.

Дедлок при высоком давлении на память. Самый коварный момент. Своп-сервер — это процесс. Когда памяти не хватает, ядро может захотеть выгрузить его собственные страницы. А куда выгружать? В своп. Который обслуживает этот самый процесс. Кольцо замкнулось, система висит. Лечится mlockall() в самом начале работы: все страницы процесса прибиваются к RAM, и ядро их больше не трогает. Без этого под нагрузкой всё встанет намертво.

GPU, который одновременно рисует и хранит своп. Если карта занята тяжёлой графикой, OpenCL-команды встают в очередь, своп подвисает, а за ним и приложения. Идея заметно лучше живёт на втором, дискретном GPU, который ничего не отображает. На ноуте с одной картой это риск, и принимать его надо осознанно.

Windows и pagefile на «съёмном» диске. Винда по умолчанию не даёт ставить файл подкачки на диски, которые считает removable. WNBD создаёт fixed-диски, так что обычно проблемы нет — но лучше проверить заранее, чем потом удивляться.

Hibernate с этой схемой не дружит. При гибернации Windows сбрасывает содержимое RAM в pagefile. Если pagefile живёт в VRAM, которая при выключении стирается, — содержимое уедет в никуда. Так что либо hibernate, либо VRAM-pagefile основным. Вместе — нет.

Что это дало бы лично мне

Я, мои 8 ГБ и хром на тридцать вкладок. Часть активна, часть — «вернусь попозже», а часть я уже и не помню, зачем открывала.

Если бы получилось отдать 4–6 ГБ VRAM под своп с высоким приоритетом, давно неактивные вкладки уезжали бы туда — быстро и незаметно. А при возврате поднимались бы из видеопамяти (по скорости примерно как с быстрого NVMe), а не перегружались из сети и не вытягивались мучительно с диска.

Сейчас переключение между задачами при нехватке памяти — это ожидание на 10–30 секунд. С VRAM-свопом, по прикидкам, секунды или доли. Разница, которую чувствуешь не в графиках, а прямо руками.

Что уже можно потрогать

Это, кстати, не чистая фантазия — кое-что придумано и до меня.

vramfs (2014) — FUSE-файловая система поверх OpenCL-буфера. Проект давно заброшен, на новых системах без напильника не заведётся, но сама концепция рабочая.

GpuRamDrive — готовое приложение под Windows: делает виртуальный диск в памяти GPU через CUDA и драйвер ImDisk. Только NVIDIA, зато компилировать ничего не надо, и pagefile на него ставится штатными средствами.

NBD + WNBD + OpenCL — сборка из открытых кусков своими руками. И Linux, и Windows, любые OpenCL-совместимые карты. Но придётся брать компилятор и разбираться.

Кому это вообще надо, а кому нет

По-честному, идея нишевая.

Есть смысл, если память распаяна и не расширяется, или если все слоты уже заняты; если есть дискретный GPU, который в твоём сценарии простаивает; и если краша по OOM не хочется, но и дёргать диск каждые пять секунд тоже не вариант.

Смысла нет, если GPU и так пашет под графику или ML; если можно просто докупить планку (тогда лучше так и сделать, серьёзно); или если нужна надёжность продакшен-уровня — для неё это всё несерьёзно.

И под конец…

Сама я это пока не собрала. Идея цепляет меня именно тем, что она про ресурс, который в некоторых сценариях реально валяется без дела. Шесть гигабайт на видяхе, пока ковыряешься в текстовом редакторе, а рядом хром душит систему, — это, согласитесь, немножко обидно.

Если кто-то уже игрался с GpuRamDrive или vramfs — расскажите, как оно на практике. И отдельно интересно: как такая схема ведёт себя под живой нагрузкой вкладками, а не в синтетике? У меня пока только теория и раздражение от диспетчера задач.

ссылка на оригинал статьи https://habr.com/ru/articles/1042028/