Каждый раз, когда встает заветный вопрос, апгрейдить ли карточки в серверной или нет, я просматриваю подобные статьи и смотрю такие видосы (нет, маркетинговым материалам от Nvidia конечно верить нельзя, как показал недавний кейс с числом CUDA-ядер).


Канал "Этот Компьютер" очень сильно недооценен, но автор не занимается ML. А в целом при анализе сравнений акселераторов для ML в глаза как правило бросаются несколько вещей:


  • Авторы учитывают как правило только "адекватность" для рынка новых карт в США;
  • Рейтинги далеки от народа и делаются на весьма стандартных сетках (что наверное в целом хорошо) без деталей;
  • Популярная мантра тренировать все более гигантские сетки вносит свои коррективы в сравнения;

Не нужно быть семи пядей во лбу, чтобы знать очевидный ответ на вопрос "а какая карта лучше?": карточки серии 20* в массы не пошли, 1080 Ti с Авито до сих очень привлекательны (и не особо дешевеют как ни странно, вероятно по этой причине).


Все это прекрасно и вряд ли стандартные бенчмарки сильно врут, но недавно я узнал про существование технологии Multi-Instance-GPU для видеокарт А100 и нативную поддержку TF32 и мне пришла идея поделиться своим опытом реального тестирования карточек на архитектуре Ampere (3090 и А100). В этой небольшой заметке я постараюсь ответить на вопросы:


  • Стоит ли свеч обновление на Ampere? (спойлер для нетерпеливых — да);
  • Стоят ли своих денег A100 (спойлер — в общем случае — нет);
  • Есть ли кейсы, когда A100 все-таки интересны (спойлер — да);
  • Полезна ли технология MIG (спойлер — да, но для инференса и для очень специфичных случаев для обучения);

За деталями прошу под кат.


Простые Вещи


Давайте сразу обратим внимание на слона в комнате. На момент написания этой заметки:


  • 3090 довольно сложно купить и продаются они примерно с 30-40% премией. Причем нехватки новых карт есть не только в СНГ;
  • A100 почти невозможно купить. Партнеры Nvidia говорили что в РФ наличии есть 1 штука, потом приедет еще несколько штук;
  • Я не особо искал, но с наскоку я не нашел информации насколько PCIE версия A100 совместима с обычными ATX платформами (именно на этот вопрос партнеры Nvidia не ответили, но я предполагаю, что в картах нет своего кулера и предполагается установка в серверное шасси с "громким" феном);
  • 3080 и более младшие модели (хотя они очень интересны по цене, а особенно для игр) не тестировали, т.к. у нас их нет, а не рассматривали их из-за размера памяти (я наивно предполагал, что получится крутить несколько сеток на 1 карте, но там все работает несколько иначе);

По понятным причинам холивор — кормить или не кормить Амазон — выносим за скобки. Уверен, что среди комментаторов найдутся свидетели церкви Амазона, но свой риг как правило "окупается" против облачных цен (особенно если собирать из дешевых компонентов) где-то за полгода-год использования.


Охлаждение


Если верить утилитам от Nvidia, то 3090 и А100 на 15-20 градусов холоднее, чем Maxwell и Pascal. Я не проводил точные замеры, но в среднем ситуация такая:


  • 4 * 1080 Ti (Pascal) с минимальными хаками по охлаждению работают в диапазоне 75-80С под 100% нагрузкой;
  • 3 * Titan X (Maxwell) работали в районе 85С под 100% нагрузкой;
  • 3 * 3090 (Ampere) работают в диапазоне 60-70С под 100% нагрузкой;
  • Нигде не применялся ни разгон, ни ограничения по питанию карт или скорости кулеров, все "из коробки";
  • Все карты имеют "турбину", то есть выталкивают тепло из корпуса;

На вопрос "почему" — есть 3 гипотезы:


  • Новый техпроцесс;
  • У 3090 немного другая форма самой карты, размер вентилятора заметно больше, размер отверстия на задней панельке сильно больше;
  • 3090 кажется тяжелее (может кто-то знает где найти точные цифры, карт нет под рукой сейчас);


Наглядная иллюстрация отличий карточек, может кто-то из комментариев подскажет диаметр вентилятора?

Наивные Метрики


Сначала, чтобы удостовериться, что драйвера работают верно (а когда они работали неверно, цифры были другие совсем), давайте протестируем все доступные карточки с помощью gpu-burn. Получается такая картина, которая очень сильно коррелирует с тем, что рисуют в обзорах.


Test GPU Gflop/s
./gpu_burn 120 Titan X (Maxwell) 4,300
./gpu_burn 120 1080 Ti (Pascal) 8,500
./gpu_burn 120 3090 (Ampere) 16,500
./gpu_burn 120 A100 (wo MIG) 16,700
./gpu-burn -tc 120 3090 (Ampere) 38,500
./gpu-burn -tc 120 A100 (wo MIG) 81,500

MIG не тестировался тут, дальше в статье увидите почему.


Цена Вопроса


Тут важно отметить, что 1080 Ti и Titan X мы покупали с рук условно "новые" (менее года использования). Не будем останавливаться лишний раз на холиворах про майнеров и политику ценообразования Nvidia, но если бережно использовать даже б/у игровые карты — их срок службы где-то 3-4 года. Цены и характеристики указаны примерные. A100 по информации от партнеров Nvidia в России в продаже имеется до нового года одна. Когда 1080Ti были доступны новыми, цены колебались примерно от 50к до 100к рублей.


GPU Mem Цена
Titan X (Maxwell) 12G 10,000 рублей (Авито)
1080 Ti 11G 25,000 рублей (Авито)
3090 (Ampere) 24G 160,000+ рублей (новая)
A100 (wo MIG) 40G US$12,500 (новая)

Думаю очевидные выводы дальше читатели сделают сами.

Пробуем 3090 и A100 c MIG


Пробуем 3090


А теперь переходим к самому интересному — к реальным тестам и собираем грабли на реальных задачах. В теории кажется, что если по памяти и вычислительным способностям 3090 или А100 в 2-3 раза превосходят 1080 Ti, то 1 такая карточка может заменить 2-3 1080 Ti и на стандартном сервере с 4 полноценными PCIE портами можно получить аналог сервера с 12 картами? Ну или можно ли взять допустим 3-4 PCIE версии A100 и получить очень мощный сервер, разделив каждую из них на несколько compute instance с использованием MIG?


Короткий ответ — нет, более длинный ответ — тоже нет, но с многочисленными оговорками.


Зачем спросите? Да просто серверные решения, которые полноценно поддерживают 8 — 16 видеокарт даже в минимальной разумной конфигурации по цене выходят в 4-5 раз дороже, чем стандартные ATX решения. А DGX Workstation или DGX продаются еще с примерно 50% премией к своей аналогичной сборке, если собирать на базе платформ от Mikrotik или Gigabyte.


Производители карт не торопятся выпускать полноценные однослотовые решения (кроме PNY c серией Quadro, но это отдельная история и скорее для дизайна или инференса). Конечно можно собрать кастомный водяной контур на 7 карточек (было несколько моделей материнских плат с 7 полноценными PCIE портами), но это "сложно" и неясно где такое размещать (да и игра не стоит свеч). С приходом PCIE 4.0 привлекательность таких решений по идее должна вырасти, но я пока не видел ничего интересного на рынке.


Пара огоровок про задачу на которой тестировали:


  • Задача — тренировка Spech-to-Text сетки на украинском датасете;
  • Из-за самой задачи экспериментально оптимальный размер батча на один процесс — 50 — не получается увеличивать без потерь в скорости сходимости;
  • Именно на этой задаче AMP у нас не работает (хотя работает на других при прочих равных, мы пока не поняли почему), но это скорее оптимизация. То есть дело не в железе, а в задаче. На других задачах — работает, поэтому вынесем за скобки;
  • Важая оговорка — поскольку по сути эта задача это sequence-to-sequence, то в общем случае построение батчей тут не совсем тривиально. Файлы разной длины попадают в батч только с файлами примерно такой же длины (чтобы снизить впустую потраченные ресурсы на обработку падинга), но размер батча статический для упрощения сравнений и более быстрой сходимости;
  • Динамический размер батча и просто его увеличение тестировали, но это особо не влияет на скорость и скорость сходимости (или ухудшает);

Вопреки тренду делать все более гигантские сети, мы наоборот занимаемся миниатюризацией наших алгоритмов и пытаемся делать наши сети все более эффективными. Поэтому вопрос параллелизации и ускорения обучения сетей интересен в первую очередь за счет увеличения числа воркеров, а не размера сетей и вычислительных возможностей ускорителей.


И тут мы наталкиваемся на первый подводный камень — Distributed Data Parallel из PyTorch (DDP, оптимальный способ масштабирования сетей на "много" видеокарт) из коробки по сути настроен только на 1 процесс на 1 карте. То есть 1 процесс может использовать 1+ карту. 2 процесса не могут использовать 1 карту, даже если там есть большой запас по IO / compute / RAM. В старых версиях драйверов явного ограничения нет и на 1080 Ti 2 процесса на 1 карте запускаются (но получется прирост по скорости всего на 5-10% вместо 40-50%). На новых картах — туда уже впилили exception.


RuntimeError: NCCL error in: /opt/conda/conda-bld/pytorch_1603729096996/work/torch/lib/c10d/ProcessGroupNCCL.cpp:784, invalid usage, NCCL version 2.7.8

Но не все так печально и плохо. Может из-за какой-то низкоуровневой магии в драйверах, может из-за TF32 (надеюсь тут знатоки подскажут), может из-за наработок в MPS 3090 ведут себя немного иначе на нашем бенчмарке:


  • При прочих равных и неизменных параметрах они используют больше памяти чем Titan X и 1080 Ti (~16 GB вместо 7-8 GB);
  • Скорость примерно в 3 раза выше, чем с Titan X (Maxwell);
  • [Нужно еще точно замерить скорость на 1080 Ti];
  • Утилизация карт на высоком уровне — более 90%;

При попытках запускать 2 DDP воркера на 1 карте мы просто получаем ошибку, при попытке тренировать 2 сетки "одновременно" мы получаем кратное замедление, при увеличении батча — прирост по скорости незначительный. Тайминги на 2 * 3090 примерно такие:


| Epoch   time, m | Type | Workers | Batch   | Params               |
|-----------------|------|---------|---------|----------------------|
| exception       | DDP  | 4       | 50 * 4  |                      |
| 3.8             | DDP  | 2       | 50 * 2  |                      |
| 3.9             | DDP  | 2       | 50 * 2  | cudnn_benchmark=True |
| 3.6             | DDP  | 2       | 100 * 2 |                      |

Для полноты рассказа, важно еще отметить что у Nvidia есть MPS который якобы позволяет крутить 2 процесса на картах без переключения контекста, а в PyTorch есть встроенный RPC-фреймворк. Но первый я просто не смог адекватно использовать без очень непонятных низкоуровневых ошибок, а второй требует радикального переписывания кода и радикально усложняет код для тренировки моделей (хотя очень интересен долгосрочно).


Так, с 3090 все понятно. Две карточки она не заменит конечно, но сама по себе, даже имея "лишнюю" память (повторюсь, мы тренируем маленькие сети), работает в 2-3 раза быстрее. Эквивалентно ли это наличию 2-3 карт, зависит от задачи.


TLDR:


  • Вы можете просто заменить карты с турбиной в своем риге на 3090 (единственный момент — в 3090 2 8-пиновых коннектора для питания, но на рынке есть блоки питания по 2000-Ватт которые точно могут запитать 4-5 таких карт, опять же никто не отменял синхронизацию 2 блоков питания);
  • При этом скорее всего температура карт снизится на 10-20 градусов Цельсия;
  • Эти карты сейчас стоят дорого и находятся в дефиците (и, наверное вряд ли пойдут в массы), но если для вас самый дорогой ресурс это время — то это интересный вариант;
  • Если большой размер памяти для вас критичен — у вас по сути нет выбора;

Пробуем A100 с MIG


Посмотрев на метрики, доступность и цену карт, А100 на первый взгляд вообще не кажется интересным вариантом, разве что в облаке на 3 дня натренировать 1 большую сетку на небольшом не сильно приватном датасете. Также если вашим алгоритмам сильно помогает AMP / FP16, то А100 может существенно добавить скорости.


Но в A100 есть интересная технология MIG (Multi Instance GPU). По сути она позволяет разбить одну "большую и мощную" карточку на набор маленьких "подкарточек" и дальше создать виртуальные Compute Instances, к которым можно обращаться как к отдельным картам.


Там довольно много деталей, за ними откройте документацию, но там доступны такие пресеты:


+--------------------------------------------------------------------------+
| GPU instance profiles:                                                   |
| GPU   Name          ID    Instances   Memory     P2P    SM    DEC   ENC  |
|                           Free/Total   GiB              CE    JPEG  OFA  |
|==========================================================================|
|   0  MIG 1g.5gb     19     0/7        4.75       No     14     0     0   |
|                                                          1     0     0   |
+--------------------------------------------------------------------------+
|   0  MIG 2g.10gb    14     0/3        9.75       No     28     1     0   |
|                                                          2     0     0   |
+--------------------------------------------------------------------------+
|   0  MIG 3g.20gb     9     0/2        19.62      No     42     2     0   |
|                                                          3     0     0   |
+--------------------------------------------------------------------------+
|   0  MIG 4g.20gb     5     0/1        19.62      No     56     2     0   |
|                                                          4     0     0   |
+--------------------------------------------------------------------------+
|   0  MIG 7g.40gb     0     0/1        39.50      No     98     5     0   |
|                                                          7     1     1   |
+--------------------------------------------------------------------------+


Доступные конфигурации

Возникает вопрос, а что если наша сетка маленькая, и A100 в теории (хотя бы на FP16) должна быть в 2 раза мощнее чем 3090? Можно ли взять 4 A100 и сделать из них допустим 12 видеокарт аналогичных по памяти и мощности 1080 Ti? Можно ли на этих многочисленных "микро-картах" тренировать нейросети так же как на нескольких обычных?


Ответим на вопросы по одному. Тут нам поможет как сама документация, так и совсем свежий блог пост от самой Nvidia.


В документации есть такой абзац:


MIG supports running CUDA applications by specifying the CUDA device on which the application should be run. With CUDA 11, only enumeration of a single MIG instance is supported.
CUDA applications treat a CI and its parent GI as a single CUDA device. CUDA is limited to use a single CI and will pick the first one available if several of them are visible. To summarize, there are two constraints:
- CUDA can only enumerate a single compute instance
- CUDA will not enumerate non-MIG GPU if any compute instance is enumerated on any other GPU
Note that these constraints may be relaxed in future NVIDIA driver releases for MIG.

Сначала, когда я его прочитал, мне показалось, что он означал только что нельзя распилить 2 карты, а можно использовать только одну. После того, как я попробовал поиграться с реальной картой, оказалось, что фреймворк внутри контейнера видит только 1 "карту" (причем видимо выбирает он только "первую"). Причем если мы внимательно прочитаем те примеры, которые Nvidia приводит в своем блоге, они по сути все относятся к сценарию "1 контейнер — 1 кусочек карты" или "тюнинг 7 маленьких моделей параллельно".


Еще там есть вот такой пассаж:


There is no GPU-to-GPU P2P (both PCIe and NVLINK) support in MIG mode, so MIG mode does not support multi-GPU or multi-node training. For large models or models trained with a large batch size, the models may fully utilize a single GPU or even be scaled to multi-GPUs or multi-nodes. In these cases, we still recommend using a full GPU or multi-GPUs, even multi-nodes, to minimize total training time.

Если использовать MIG по прямому назначению, то есть делить карту на физические кусочки (slices), назначать им Compute Instances и прокидывать их в изолированные контейнеры — то все работает как надо. It just works.


Итоговые Замеры


Тут не совсем идеальные сравнения (на Титане у меня был DP а не DDP), да и на A100 в итоге я не стал гонять эксперименты на 10, 20, 30 часов и впустую (зачем греть атмосферу), но я замерил время на 1 эпохе.



Когда крутишь 1 сетку на A100 утилизация не достигает даже и половины — ну то есть если бы ее можно было распилить на 2-3 карты, все было бы прекрасно

Avg epoch time, m Workers Batch GPUs CER @10 hours CER @20 h CER @30 h Comment
4.7 2, DDP 50 * 2 2 * 3090 14.4 12.3 11.44 Close to 100% utilization
15.3 1, DP 50 2 * Titan X 21.6 17.4 15.7 Close to 100% utilization
11.4 1, DDP 50 * 1 1 * A100 NA NA NA About 35-40% utilization
TBD 2, DDP 50 * 2 2 * 1080 Ti TBD TBD TBD

На 1080 Ti ресурсы были только чтобы прогнать 1 эпоху.


Выводы


Выводы про 3090:


  • Если вынести за скобки вопрос доступности, то апгрейд стоит делать. Вы получите минимум x2 по скорости. Если у вас работает AMP — то может даже и все x3-x4;
  • С учетом роста производительности, цена кажется немного завышенной, но не заоблачной. Понижение цены где-то на 30-40% как мне кажется было бы адекватным;
  • Когда выходило новое поколение карт все беспокоились насчет охлаждения. Она на удивление холодная;
  • Единственная беда — карточка просит 2 8-пиновых коннектора для питания;

Выводы про A100:


  • Если судить по цене деленной на производительность, карта не очень интересная (наценка в 2-3 раза против 3090);
  • То, что Nvidia сделала технологию для эффективного использования для инференса — это круто, а то карты стали уж слишком большими и крутыми;
  • Если вы можете использовать обычные игровые карты (те же 1080 Ti или PNY Quadro) для инференса, то они представляют сильно больший value for money;
  • Есть большой нераскрытый потенциал в развитии технологии MIG;
  • Если вам нужно реально 40 GB памяти и много compute, то альтернатив особо нет;
  • Неясен вопрос с установкой PCIE версии в обычные ATX корпуса без кастома, "колхоза" или воды ;

Update 1


Добавил gpu-burn вместе с CUDA_VISIBLE_DEVICES
Попробую указать CUDA_VISIBLE_DEVICES внутри каждого процесса PyTorch


Test GPU Gflop/s RAM
./gpu_burn 120 A100 // 7 2,400 * 7 4.95 * 7
./gpu_burn 120 A100 // 3 4,500 * 3 9.75 * 3
./gpu_burn 120 A100 // 2 6,700 * 2 19.62 * 2
./gpu_burn 120 A100 (wo MIG) 16,700 39.50 * 1
./gpu-burn -tc 120 A100 // 7 15,100 * 7 4.95 * 7
./gpu-burn -tc 120 A100 // 3 30,500 * 3 9.75 * 3
./gpu-burn -tc 120 A100 // 2 42,500 * 2 19.62 * 2
./gpu-burn -tc 120 A100 (wo MIG) 81,500 39.50 * 1

Update 2


График утилизации карты когда запущено 3 параллельных gpu-burn через MIG


Update 3


В итоге я смог завести DDP с MIG на PyTorch.
Надо было сделать так и везде использовать нулевой (первый) дивайс.


def main(rank, args):
    os.environ["CUDA_VISIBLE_DEVICES"] = args.ddp.mig_devices[rank]
    import torch
    ...

С NCCL я получил такой же exception. Поменяв nccl на gloo оно запустилось… но рабортало оооочень медленно. Ну условно в десять раз медленнее и утилизация карты была на очень низком уровне. Думаю нет смысла дальше копать.