Скорость вычислений WebGPU в сравнении с WebGL
WebGPU — сменщик WebGL, новый API для работы с графическим ускорителем в браузере и появится в публичном Chrome в начале 2022 года. По сравнению с WebGL он обещает лучшее быстродействие и лучшую совместимость с последними моделями видеокарт, но самой большим нововведением WebGPU является специальный функционал для вычислений на GPU.
Но разве WebGL не имел такой возможности?
И да и нет. Вычисления на WebGL сделаны через хак. Данные превращаются в изображение, которое загружается в WebGL как текстура, затем в пиксельном шейдере производятся вычисления и результат отображается в виде набора пикселов на канвасе. Считывание результатов производится с помощью getPixelsData. Выглядит не очень эффективно, да?
В чем отличие WebGPU?
API который представляет WebGPU для вычислений (compute shaders) имеет несколько отличий которые на первый взгляд мало значительны, но при детальном рассмотрении дают разработчику новые возможности.
Отличия
- Данные не нужно преобразовывать в текстуру
- Вычисление выполняется асинхронно и не блокирует главный поток JS (привет сложная симуляция физики на 60FPS)
- Нам больше не нужен элемент canvas и связанные с его размером ограничения, вычисления производятся в памяти
- Нам не нужно выполнять дорогой и синхронный getPixelsData
- Нам не нужно конвертировать полученные значения пикселей в данные
Итого WebGPU обещает нам что вычисления можно будет делать не блокируя main thread и ощутимо быстрее, но насколько именно?
Как будем тестировать?
Для проверки используем умножение матриц, как тип вычислений, который достаточно легко масштабируется и создает необходимую нагрузку.
Для примера умножение матриц 16х16 требует 7936 операций умножения, а 60х60 уже 428400.
Я адаптировал существующий WebGL код для умножения матриц и код для вычислений на WebGPU. Запускать не текущий момент можно только в Chrome Canary с включенным флагом #unsafe-webgpu-enabled
Результаты
Первый результат был неожиданным:
После определенного размера матрицы WebGL начал обходить WebGPU. Оказалось что размер working group (количество одновременно выполняемых вычислений) был выставлен в коде так, что всегда был равен размеру матрицы. То есть при матрице 1024х1024 в шейдер отправлялся блок соответствующего размера и тк количество арифметических блоков на моем GPU равен 256 то вычисления выполнялись не оптимально, в несколько этапов. Количество ALU можно получить в коде как свойство WebGPU контекста maximumWorkingGroupSize. Как только я ограничил размер working group до 256 получил:
Ожидаемо, из-за отсутствия необходимости в создании текстур и чтения данных с canvas накладные расходы на инициализацию у WebGPU значительно ниже, а быстродействие выше и с ростом объема вычислений эта разница только растет. Стоит отметить, что и WebGL и WebGPU требуют времени на разогрев.
Вывод
Мы экспериментально подтвердили, что по сравнению с WebGL вычисления на WebGPU имеет меньшую стоимость инициализации и выполняются в среднем в три раза быстрее кроме того из-за асинхронного API позволяют разгрузить главный поток браузера. Это позволяет выполнять в браузере новые типы задач: редактирование видео и аудио, симуляция физических процессов и более реалистичных визуальных эффектов — вот неполный список того, где с приходом WebGPU стоит ожидать новых, невозможных ранее решений.