Продолжаю изучать посты от Horace He - разработчика Pytorch. Данный пост начинается со следующего твита Карпатого:
Самое большое ускорение для nanoGPT (около 25%) - это увеличение размера словаря с 50257 до 50304 - числа, кратного 64
Такое стало возможно из-за особенностей устройства GPU. На прикреплённой картинке вы можете видеть зависимость эффективности (операций в секунду) матричного умножения от их размера. Сегодня я кратко перескажу об источниках эффектов, дающих в сумме такую картину.
Итак, самый главный тренд - это общее увеличение эффективности при увеличении размера матрицы. Про GPU важно понимать одну вещь - само применение операции на загруженных данных занимает далеко не 100% времени.
Во-первых, каждая операция накладывает какой-то фиксированный overhead, связанный с общей координации работы GPU и запуском вычислительного "ядра".
Во-вторых, значительный кусок времени тратится на перемещение объектов в памяти. С увеличением N - размера матрицы, количество элементов увеличивается как N^2, тогда как количество расчётов растёт быстрее. Таким образом, при увеличении N затраты на перемещение - то, что GPU умеет делать хуже, растут медленнее, чем затраты на компьют, который GPU умеет делать лучше.
На этот тренд накладываются 2 дополнительных эффекта вызывающих разрывы в графике эффективности.
Tiling - это единственный эффект, который может приводить к тому, что увеличение размера матрицы приводит к уменьшению абсолютных затрат на операцию - то, о чём писал Карпатый.
Эффект объясняется просто - данные в кэшах GPU хранятся блоками по 2^X элементов. Если вы храните в нём матрицу 2^X * 2^X, то каждую строчку можно прочитать за 1 обращение к блоку памяти. А вот с матрицей (2^X-1) * (2^X-1) всё плохо - большая часть строчек будет занимать частично 2 блока памяти, и для её считывания GPU нужно прочитать оба и обрезать лишнее.
Чем на большую степень двойки делится ваша матрица, тем более удачно она будет хранится в разного рода кэшах и видеопамяти, и тем меньше операций чтения необходимо совершить для проведения расчётов. На графике явно выделяются 4 горизонтальные группы точек. Верхняя - это кратные 16, далее идут кратные 8, кратные 2 и нечётные.
Последний эффект - Wave quantization - волнообразное изменение эффективности, с пиками в числах, у которых остаток от деления на 256 равен 128. Природа этого явления уже другая.
Грубо говоря, если у вас есть N параллельных процессоров, то для выполнения N+1 операций вам нужно в 2 раза больше времени, чем для N операций - надо провести 2 "волны" операций вместо одной. Пик эффективности расчётов на данном GPU достигается при использовании всех ядер во время каждой вычислительной волны, и он достигается при N mod 256 = 128
Как видите, понимание нижестоящих этажей технологического небоскрёба, в котором мы живём, позволяет уменьшать неэффективность. Может быть, когда-нибудь надо всё-таки пройти курс по программированию на куде... Но не на этой неделе.
@knowledge_accumulator
