Публикация была переведена автоматически. Исходный язык: Русский
Как разработчики, мы всегда ищем возможности улучшить произвоительность приложения. Когда дело доходит до web-приложении, мы, в основном, вносим улучшения в коде.
Но задумывались ли вы когда-нибудь скомбинировать мощь графического процессора в своих web-приложениях для повышения производительности?
Эта статья познакомит вас с библиотекой ускорения JavaScript под названием GPU.js и покажет, как улучшить сложные вычисления.
***
Что такое GPU.js и зачем нам его использовать?

Источник: https://gpu.rocks/#/
В кратце, GPU.js – это библиотека ускорения JavaScript, которую можно использовать для вычислений общего назначения на графических процессорах с использованием JavaScript. Он поддерживается в браузерах, Node.js и TypeScript.
Помимо повышения производительности, я рекомендую использовать GPU.js по нескольким причинам:
- GPU.js использует JavaScript в качестве основы, что позволяет использовать синтаксис JavaScript;
- Он берет на себя ответственность за автоматическую трансляцию JavaScript в язык шейдеров и компилирует их;
- Он может вернуться к обычному движку JavaScript, если в устройстве нет графического процессора. Так что, неудобств в использовании GPU.js. не будет;
- GPU.js также можно использовать для параллельных вычислений. Кроме того, вы можете выполнять несколько вычислений асинхронно как в CPU, так и в GPU одновременно.
Учитывая все это, не вижу причин не использовать GPU.js. Так что, давайте посмотрим, как мы можем начать с этим.
***
Как установить GPU.js?
Установка GPU.js аналогична установке любой другой библиотеки JavaScript.
Для Node.js проекта:
npm install gpu.js --save --- или --- yarn add gpu.js
const { GPU } = require('gpu.js')
--- или ---
import { GPU } from 'gpu.js'
const gpu = new GPU()
Для браузера:
Скачайте GPU.js локально или используйте CDN.
<script src="dist/gpu-browser.min.js"></script> --- или --- <script src="//unpkg.com/gpu.js@latest/dist/gpu- browser.min.js"></script> <script src="//cdn.jsdelivr.net/npm/gpu.js@latest/dist/gpu-browser.min.js"></script> <script> const gpu = new GPU() ... </script>
Примечание. Если вы используете Linux, вам необходимо убедиться, что у вас установлены правильные файлы, выполнив:
sudo apt install mesa-common-dev libxi-dev
Вот всё что вам нужно знать об установке и импорте GPU.js. Теперь вы можете начать использовать GPU в своем приложении.
Кроме того, я настоятельно рекомендую разобраться в основных функциях и концепциях GPU.js. Итак, давайте начнем с нескольких основ GPU.js.
***
Совет: создавайте и делитесь независимыми компонентами с Bit
Bit – это ультра-расширяемый инструмент, который позволяет создавать по-настоящему модульные приложения с независимо созданными, версионированными и поддерживаемыми компонентами.
Используйте его для создания модульных приложений и дизайн-системах, создания и доставки микро-интерфейсов или просто обмена компонентами между приложениями.

Компоненты Material UI доступны индивидуально на Bit.dev
***
Создание функции
Вы можете определить функции в GPU.js для запуска в GPU, используя общий синтаксис JavaScript.
const exampleKernel = gpu.createKernel(function () {
...
}, settings);
В приведенном выше примере кода показана базовая структура функции GPU.js. Я назвал функцию exampleKernel. Как видно, я использовал метод createKernel, которая выполняет вычисления с использованием графического процессора.
Также обязательно определите размер вывода. В приведенном выше примере я использовал параметр, называемый settings, для назначения размера вывода.
const settings = {
output: [100]
};
Выходные данные функции ядра могут быть одномерные, двухмерные или трёхмерные, что означает, что у нее может быть до 3 потоков. Вы можете получить доступ к этим потокам в ядре с помощью команды this.thread.
- 1D: [длина] –
value[this.thread.x] - 2D: [ширина, высота] –
value[this.thread.y] [this.thread.x] - 3D: [ширина, высота, глубина] –
value[this.thread.z] [this.thread.y] [this.thread.x]
Наконец, созданная функция может быть вызвана, как и любая другая функция JavaScript, с использованием имени функции: exampleKernel()
***
Поддерживаемые переменные в Kernel
Числа
В функции GPU.js можно использовать любое целое число или число с плавающей запятой.const exampleKernel = gpu.createKernel(function () {
const number1 = 10;
const number2 = 0.10;
return number1 + number2;
}, settings);
Булевые
Булевые значения также поддерживаются в GPU.js, как и в JavaScript.const kernel = gpu.createKernel(function () {
const bool = true;
if (bool) {
return 1;
} else {
return 0;
}
},settings);
Массивы
Вы можете определять числовые массивы любого размера в функциях kernel и возвращать их.const exampleKernel = gpu.createKernel(function () {
const array1 = [0.01, 1, 0.1, 10];
return array1;
}, settings);
Функции
Использование приватных функций внутри функции kernel также возможно в GPU.js.const exampleKernel = gpu.createKernel(function () {
function privateFunction() {
return [0.01, 1, 0.1, 10];
}
return privateFunction();
}, settings);
***
Поддерживаемые типы входных данных
В дополнение к указанным выше типам переменных, вы можете передавать функциям kernel несколько типов входных данных.
Числа
Подобно объявлению переменной, вы можете передавать целые числа или числа с плавающей запятой в функции kernel, как показано ниже.
const exampleKernel = gpu.createKernel(function (x) {
return x;
}, settings);
exampleKernel(25);
Одномерные, двухмерные или трёхмерные массивы чисел
Вы можете передавать типы массивов Array, Float32Array, Int16Array, Int8Array, Uint16Array, uInt8Array в kernel GPU.js.
const exampleKernel = gpu.createKernel(function (x) {
return x;
}, settings);
exampleKernel([1, 2, 3]);
Предварительно выровненные двухмерные и трёхмерные массивы также принимаются функциями ядра. Такой подход значительно ускоряет загрузку, и для этого вам нужно использовать параметр ввода из GPU.js.
const { input } = require('gpu.js');
const value = input(flattenedArray, [width, height, depth]);
HTML Изображения
Передача изображений в функции – это новая вещь, которую мы можем увидеть в GPU.js по сравнению с традиционным JavaScript. С помощью GPU.js вы можете передать одно или несколько изображений HTML в виде массива функции kernel.
// Одно изображение
const kernel = gpu.createKernel(function (image) {
...
})
.setGraphical(true)
.setOutput([100, 100]);
const image = document.createElement('img');
image.src = 'image1.png';
image.onload = () => {
kernel(image);
document.getElementsByTagName('body')[0].appendChild(kernel.canvas);
};
// Несколько изображении
const kernel = gpu.createKernel(function (image) {
const pixel = image[this.thread.z][this.thread.y][this.thread.x];
this.color(pixel[0], pixel[1], pixel[2], pixel[3]);
})
.setGraphical(true)
.setOutput([100, 100]);
const image1 = document.createElement('img');
image1.src = 'image1.png';
image1.onload = onload;
....
// Добавить ещё изображения
....
const totalImages = 3;
let loadedImages = 0;
function onload () {
loadedImages++;
if (loadedImages === totalImages) {
kernel([image1, image2, image3]);
document.getElementsByTagName('body')[0].appendChild(kernel.canvas);
}
};
Помимо вышеперечисленных конфигураций, есть много интересных вещей для экспериментов с GPU.js. Вы можете найти их в их документации. Поскольку теперь вы понимаете несколько конфигураций, давайте напишем функцию с помощью GPU.js и сравним ее производительность.
***
Первая функция с использованием GPU.js
Объединив все, что мы обсуждали ранее, я написал небольшое Angular приложение для сравнения производительности вычислений GPU и CPU путем умножения двух массивов на 1000 элементов.
Шаг 1 - Функция для создания числовых массивов из 1000 элементов
Я сгенерирую 2D-массив с 1000 числами для каждого элемента и использую их для вычислений на следующих шагах.
generateMatrices() {
this.matrices = [[], []];
for (let y = 0; y < this.matrixSize; y++) {
this.matrices[0].push([])
this.matrices[1].push([])
for (let x = 0; x < this.matrixSize; x++) {
const value1 = parseInt((Math.random() * 10).toString())
const value2 = parseInt((Math.random() * 10).toString())
this.matrices[0][y].push(value1)
this.matrices[1][y].push(value2)
}
}
}
Шаг 2 – функция ядра
Это самая важная функция в этом приложении, поскольку все вычисления графического процессора происходят внутри него. Здесь функция multiplyMatrix получит в качестве входных данных два числовых массива и размер матрицы. Затем он умножит два массива и вернет общую сумму измеряя время с помощью Performance API.
gpuMultiplyMatrix() {
const gpu = new GPU();
const multiplyMatrix = gpu.createKernel(function (a: number[][], b: number[][], matrixSize: number) {
let sum = 0;
for (let i = 0; i < matrixSize; i++) {
sum += a[this.thread.y][i] * b[i][this.thread.x];
}
return sum;
}).setOutput([this.matrixSize, this.matrixSize])
const startTime = performance.now();
const resultMatrix = multiplyMatrix(this.matrices[0], this.matrices[1], this.matrixSize);
const endTime = performance.now();
this.gpuTime = (endTime - startTime) + " ms";
console.log("GPU TIME : "+ this.gpuTime);
this.gpuProduct = resultMatrix as number[][];
}
Шаг 3 – функция умножения ЦП.
Это традиционная функция TypeScript, используемая для измерения времени вычислений для одних и тех же массивов.
cpuMutiplyMatrix() {
const startTime = performance.now();
const a = this.matrices[0];
const b = this.matrices[1];
let productRow = Array.apply(null, new Array(this.matrixSize)).map(Number.prototype.valueOf, 0);
let product = new Array(this.matrixSize);
for (let p = 0; p < this.matrixSize; p++) {
product[p] = productRow.slice();
}
for (let i = 0; i < this.matrixSize; i++) {
for (let j = 0; j < this.matrixSize; j++) {
for (let k = 0; k < this.matrixSize; k++) {
product[i][j] += a[i][k] * b[k][j];
}
}
}
const endTime = performance.now();
this.cpuTime = (endTime — startTime) + “ ms”;
console.log(“CPU TIME : “+ this.cpuTime);
this.cpuProduct = product;
}
Вы можете найти полный демо-проект на моём GitHub аккаунте.
CPU vs GPU – Сравнение производительности
А теперь пора посмотреть, верна ли вся эта шумиха вокруг GPU.js и вычислений на GPU. Поскольку в предыдущем разделе я создал приложение Angular, я использовал его для измерения производительности.

Как вы можете увидеть, программа на GPU потребовало всего 799 мс для вычислений, в то время как на CPU заняло 7511 мс, что почти в 10 раз больше.
Не останавливаясь на этом, я провел те же тесты в течение пары циклов, изменяя размер массива.

Во-первых, я попробовал использовать массивы меньшего размера и заметил, что CPU занимает меньше времени, чем графический процессор. Например, когда я уменьшил размер массива до 10 элементов, CPU потребовалось всего 0,14 мс, а GPU - 108 мс.
Но по мере увеличения размера массива наблюдался явный разрыв между временем, затрачиваемым на GPU и CPU. Как вы можете видеть на графике выше, GPU оказался победителем.
***
Вывод
Основываясь на моем эксперименте с использованием GPU.js, он может повысить производительность приложений JavaScript.
Но мы должны помнить об использовании GPU только для сложных задач. В противном случае мы будем тратить ресурсы впустую и, в конечном итоге, снизим производительность приложения, как показано на приведенном выше графике.
Однако, если вы еще не пробовали GPU.js, я приглашаю вас всех попробовать его и поделиться своим опытом.
Спасибо, что прочитали!!!
***
От переводчика
Статься переведена с английского на Medium: Оригинальная статья
Это мой первый пост на этой площадке, так что буду рад вашим советам!
Как разработчики, мы всегда ищем возможности улучшить произвоительность приложения. Когда дело доходит до web-приложении, мы, в основном, вносим улучшения в коде.
Но задумывались ли вы когда-нибудь скомбинировать мощь графического процессора в своих web-приложениях для повышения производительности?
Эта статья познакомит вас с библиотекой ускорения JavaScript под названием GPU.js и покажет, как улучшить сложные вычисления.
***
Что такое GPU.js и зачем нам его использовать?

Источник: https://gpu.rocks/#/
В кратце, GPU.js – это библиотека ускорения JavaScript, которую можно использовать для вычислений общего назначения на графических процессорах с использованием JavaScript. Он поддерживается в браузерах, Node.js и TypeScript.
Помимо повышения производительности, я рекомендую использовать GPU.js по нескольким причинам:
- GPU.js использует JavaScript в качестве основы, что позволяет использовать синтаксис JavaScript;
- Он берет на себя ответственность за автоматическую трансляцию JavaScript в язык шейдеров и компилирует их;
- Он может вернуться к обычному движку JavaScript, если в устройстве нет графического процессора. Так что, неудобств в использовании GPU.js. не будет;
- GPU.js также можно использовать для параллельных вычислений. Кроме того, вы можете выполнять несколько вычислений асинхронно как в CPU, так и в GPU одновременно.
Учитывая все это, не вижу причин не использовать GPU.js. Так что, давайте посмотрим, как мы можем начать с этим.
***
Как установить GPU.js?
Установка GPU.js аналогична установке любой другой библиотеки JavaScript.
Для Node.js проекта:
npm install gpu.js --save --- или --- yarn add gpu.js
const { GPU } = require('gpu.js')
--- или ---
import { GPU } from 'gpu.js'
const gpu = new GPU()
Для браузера:
Скачайте GPU.js локально или используйте CDN.
<script src="dist/gpu-browser.min.js"></script> --- или --- <script src="//unpkg.com/gpu.js@latest/dist/gpu- browser.min.js"></script> <script src="//cdn.jsdelivr.net/npm/gpu.js@latest/dist/gpu-browser.min.js"></script> <script> const gpu = new GPU() ... </script>
Примечание. Если вы используете Linux, вам необходимо убедиться, что у вас установлены правильные файлы, выполнив:
sudo apt install mesa-common-dev libxi-dev
Вот всё что вам нужно знать об установке и импорте GPU.js. Теперь вы можете начать использовать GPU в своем приложении.
Кроме того, я настоятельно рекомендую разобраться в основных функциях и концепциях GPU.js. Итак, давайте начнем с нескольких основ GPU.js.
***
Совет: создавайте и делитесь независимыми компонентами с Bit
Bit – это ультра-расширяемый инструмент, который позволяет создавать по-настоящему модульные приложения с независимо созданными, версионированными и поддерживаемыми компонентами.
Используйте его для создания модульных приложений и дизайн-системах, создания и доставки микро-интерфейсов или просто обмена компонентами между приложениями.

Компоненты Material UI доступны индивидуально на Bit.dev
***
Создание функции
Вы можете определить функции в GPU.js для запуска в GPU, используя общий синтаксис JavaScript.
const exampleKernel = gpu.createKernel(function () {
...
}, settings);
В приведенном выше примере кода показана базовая структура функции GPU.js. Я назвал функцию exampleKernel. Как видно, я использовал метод createKernel, которая выполняет вычисления с использованием графического процессора.
Также обязательно определите размер вывода. В приведенном выше примере я использовал параметр, называемый settings, для назначения размера вывода.
const settings = {
output: [100]
};
Выходные данные функции ядра могут быть одномерные, двухмерные или трёхмерные, что означает, что у нее может быть до 3 потоков. Вы можете получить доступ к этим потокам в ядре с помощью команды this.thread.
- 1D: [длина] –
value[this.thread.x] - 2D: [ширина, высота] –
value[this.thread.y] [this.thread.x] - 3D: [ширина, высота, глубина] –
value[this.thread.z] [this.thread.y] [this.thread.x]
Наконец, созданная функция может быть вызвана, как и любая другая функция JavaScript, с использованием имени функции: exampleKernel()
***
Поддерживаемые переменные в Kernel
Числа
В функции GPU.js можно использовать любое целое число или число с плавающей запятой.const exampleKernel = gpu.createKernel(function () {
const number1 = 10;
const number2 = 0.10;
return number1 + number2;
}, settings);
Булевые
Булевые значения также поддерживаются в GPU.js, как и в JavaScript.const kernel = gpu.createKernel(function () {
const bool = true;
if (bool) {
return 1;
} else {
return 0;
}
},settings);
Массивы
Вы можете определять числовые массивы любого размера в функциях kernel и возвращать их.const exampleKernel = gpu.createKernel(function () {
const array1 = [0.01, 1, 0.1, 10];
return array1;
}, settings);
Функции
Использование приватных функций внутри функции kernel также возможно в GPU.js.const exampleKernel = gpu.createKernel(function () {
function privateFunction() {
return [0.01, 1, 0.1, 10];
}
return privateFunction();
}, settings);
***
Поддерживаемые типы входных данных
В дополнение к указанным выше типам переменных, вы можете передавать функциям kernel несколько типов входных данных.
Числа
Подобно объявлению переменной, вы можете передавать целые числа или числа с плавающей запятой в функции kernel, как показано ниже.
const exampleKernel = gpu.createKernel(function (x) {
return x;
}, settings);
exampleKernel(25);
Одномерные, двухмерные или трёхмерные массивы чисел
Вы можете передавать типы массивов Array, Float32Array, Int16Array, Int8Array, Uint16Array, uInt8Array в kernel GPU.js.
const exampleKernel = gpu.createKernel(function (x) {
return x;
}, settings);
exampleKernel([1, 2, 3]);
Предварительно выровненные двухмерные и трёхмерные массивы также принимаются функциями ядра. Такой подход значительно ускоряет загрузку, и для этого вам нужно использовать параметр ввода из GPU.js.
const { input } = require('gpu.js');
const value = input(flattenedArray, [width, height, depth]);
HTML Изображения
Передача изображений в функции – это новая вещь, которую мы можем увидеть в GPU.js по сравнению с традиционным JavaScript. С помощью GPU.js вы можете передать одно или несколько изображений HTML в виде массива функции kernel.
// Одно изображение
const kernel = gpu.createKernel(function (image) {
...
})
.setGraphical(true)
.setOutput([100, 100]);
const image = document.createElement('img');
image.src = 'image1.png';
image.onload = () => {
kernel(image);
document.getElementsByTagName('body')[0].appendChild(kernel.canvas);
};
// Несколько изображении
const kernel = gpu.createKernel(function (image) {
const pixel = image[this.thread.z][this.thread.y][this.thread.x];
this.color(pixel[0], pixel[1], pixel[2], pixel[3]);
})
.setGraphical(true)
.setOutput([100, 100]);
const image1 = document.createElement('img');
image1.src = 'image1.png';
image1.onload = onload;
....
// Добавить ещё изображения
....
const totalImages = 3;
let loadedImages = 0;
function onload () {
loadedImages++;
if (loadedImages === totalImages) {
kernel([image1, image2, image3]);
document.getElementsByTagName('body')[0].appendChild(kernel.canvas);
}
};
Помимо вышеперечисленных конфигураций, есть много интересных вещей для экспериментов с GPU.js. Вы можете найти их в их документации. Поскольку теперь вы понимаете несколько конфигураций, давайте напишем функцию с помощью GPU.js и сравним ее производительность.
***
Первая функция с использованием GPU.js
Объединив все, что мы обсуждали ранее, я написал небольшое Angular приложение для сравнения производительности вычислений GPU и CPU путем умножения двух массивов на 1000 элементов.
Шаг 1 - Функция для создания числовых массивов из 1000 элементов
Я сгенерирую 2D-массив с 1000 числами для каждого элемента и использую их для вычислений на следующих шагах.
generateMatrices() {
this.matrices = [[], []];
for (let y = 0; y < this.matrixSize; y++) {
this.matrices[0].push([])
this.matrices[1].push([])
for (let x = 0; x < this.matrixSize; x++) {
const value1 = parseInt((Math.random() * 10).toString())
const value2 = parseInt((Math.random() * 10).toString())
this.matrices[0][y].push(value1)
this.matrices[1][y].push(value2)
}
}
}
Шаг 2 – функция ядра
Это самая важная функция в этом приложении, поскольку все вычисления графического процессора происходят внутри него. Здесь функция multiplyMatrix получит в качестве входных данных два числовых массива и размер матрицы. Затем он умножит два массива и вернет общую сумму измеряя время с помощью Performance API.
gpuMultiplyMatrix() {
const gpu = new GPU();
const multiplyMatrix = gpu.createKernel(function (a: number[][], b: number[][], matrixSize: number) {
let sum = 0;
for (let i = 0; i < matrixSize; i++) {
sum += a[this.thread.y][i] * b[i][this.thread.x];
}
return sum;
}).setOutput([this.matrixSize, this.matrixSize])
const startTime = performance.now();
const resultMatrix = multiplyMatrix(this.matrices[0], this.matrices[1], this.matrixSize);
const endTime = performance.now();
this.gpuTime = (endTime - startTime) + " ms";
console.log("GPU TIME : "+ this.gpuTime);
this.gpuProduct = resultMatrix as number[][];
}
Шаг 3 – функция умножения ЦП.
Это традиционная функция TypeScript, используемая для измерения времени вычислений для одних и тех же массивов.
cpuMutiplyMatrix() {
const startTime = performance.now();
const a = this.matrices[0];
const b = this.matrices[1];
let productRow = Array.apply(null, new Array(this.matrixSize)).map(Number.prototype.valueOf, 0);
let product = new Array(this.matrixSize);
for (let p = 0; p < this.matrixSize; p++) {
product[p] = productRow.slice();
}
for (let i = 0; i < this.matrixSize; i++) {
for (let j = 0; j < this.matrixSize; j++) {
for (let k = 0; k < this.matrixSize; k++) {
product[i][j] += a[i][k] * b[k][j];
}
}
}
const endTime = performance.now();
this.cpuTime = (endTime — startTime) + “ ms”;
console.log(“CPU TIME : “+ this.cpuTime);
this.cpuProduct = product;
}
Вы можете найти полный демо-проект на моём GitHub аккаунте.
CPU vs GPU – Сравнение производительности
А теперь пора посмотреть, верна ли вся эта шумиха вокруг GPU.js и вычислений на GPU. Поскольку в предыдущем разделе я создал приложение Angular, я использовал его для измерения производительности.

Как вы можете увидеть, программа на GPU потребовало всего 799 мс для вычислений, в то время как на CPU заняло 7511 мс, что почти в 10 раз больше.
Не останавливаясь на этом, я провел те же тесты в течение пары циклов, изменяя размер массива.

Во-первых, я попробовал использовать массивы меньшего размера и заметил, что CPU занимает меньше времени, чем графический процессор. Например, когда я уменьшил размер массива до 10 элементов, CPU потребовалось всего 0,14 мс, а GPU - 108 мс.
Но по мере увеличения размера массива наблюдался явный разрыв между временем, затрачиваемым на GPU и CPU. Как вы можете видеть на графике выше, GPU оказался победителем.
***
Вывод
Основываясь на моем эксперименте с использованием GPU.js, он может повысить производительность приложений JavaScript.
Но мы должны помнить об использовании GPU только для сложных задач. В противном случае мы будем тратить ресурсы впустую и, в конечном итоге, снизим производительность приложения, как показано на приведенном выше графике.
Однако, если вы еще не пробовали GPU.js, я приглашаю вас всех попробовать его и поделиться своим опытом.
Спасибо, что прочитали!!!
***
От переводчика
Статься переведена с английского на Medium: Оригинальная статья
Это мой первый пост на этой площадке, так что буду рад вашим советам!