[:en]WebGPU computations performance in comparison to WebGL[:ru]Скорость вычислений WebGPU в сравнении с WebGL[:]
[:en]WebGPU – the successor of WebGL, a brand new API to utilize GPUs in the browser. It is promised to be available in regular Chrome in Q1 2022. In comparison to WebGL, WebGPU promises better performance and better compatibility with modern hardware, but the most recognizable feature of WebGPU is a special API for performing computations on GPU.
Does not WebGL have the same feature?
Yes and no. WebGL does not have a special API for computation but still, there is a hack that makes it possible. Data is being converted into an image, image uploaded to GPU as a texture, texture rendered synchronously with a pixel shader that does an actual computation. Then the result of computation we have as a set of pixels on a <canvas> element and we have to read it synchronously with getPixelsData then color codes to be converted back to your data. Looks like an inefficient mess, right?
How WebGPU is different?
API WebGPU provides for computations (compute shaders) is different in the way it is easy to miss the importance of the improvements, however, it empowers developers with absolutely new features. The way it works is:
The differences
- Data uploaded to GPU as a buffer, you do not convert it to pixels so it is cheaper
- Computation is being performed asynchronously and does not block JS main thread (say hi to real-time post-processing and complex physics simulation at 60FPS)
- We do not need canvas element and we avoid its limitation on size
- We do not do expensive and synchronous getPixelsData
- We do not spend time converting pixels values back to data
So WebGPU’s promise is that we can compute without blocking the main thread and compute faster, but how much faster?
How do we benchmark?
As a benchmark, we use matrix multiplication which lets us scale the complexity and amount of computations easily.
For example, 16×16 matrix multiplication requires 7936 multiplication operations and 60×60 already gets us 428400 operations.
Sure thing we run the test in an appropriate browser which is Chrome Canary with #unsafe-webgpu-enabled flag on.
Results
The first results were discouraging and WebGL outperformed WebGPU at the bigger numbers:
Then I found that the size of a working group (number of operations to calculate in a single batch) is set in code to be as big as the matrix side. It works fine until the matrix side is lower than the number of ALUs on GPU (arithmetic logical unit) which is reflected in WebGPU API as a maximumWorkingGroupSize property. For me, it was 256. Once the working group was set to be less or equal to 256 this is the result we get:
This is quite impressive while is expected. WebGPU initialization and data transfer times are remarkably lower because we do not convert data to textures and do not read it from pixels. WebGPU performance is significantly higher and gets to 3.5x faster compared to WebGL while it does not block the main thread.
It is also interesting to see WebGL failing after matrix size gets over 4096×4096 because of canvas and texture size limitations while WebGPU is capable to perform for matrices up to 5000×5000 which sounds not much of a difference but actually is 112552823744 more operations to perform and 817216 more values to hold.
Small but interesting fact – both WebGL / WebGPU require some time to warm up while JS goes full power straight away.
Conclusion
The experiment proved that WebGPU compute shaders are in practice 3.5x faster than WebGL computing with pixel shaders while having significantly higher limits regarding the amount of data to process also it does not block the main thread. This allows new kinds of tasks in the browser: video and audio editing, real-time physics simulation, and more realistic visual effects, machine learning. This is the incomplete list of jobs to benefit from WebGPU where we can expect the new generation of apps to appear and the boundaries of what is possible to do on the Web significantly expanded.[:ru]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 стоит ожидать новых, невозможных ранее решений.[:]