Как оптимизировать код на JS для ускорения работы?

Доброго дня.

Стоит задача рассчитать значения матрицы 52000 на 52000 на JS.
Значение в каждой ячейке является результатом вычисления Косинусовой близости векторов размерностью 300.

Для ускорения вычислений использую GPUJS:

gpu.addFunction(function mF(a, b) {
            return a * b;
        });
const f3 = gpu.createKernel(function(inp) {

            var a = 0
            var b = 0
            var c = 0
            for (var i = 0; i < 300; i++) {
                a += mF(inp[this.thread.y][i], inp[this.thread.x][i])
                b += mF(inp[this.thread.y][i], inp[this.thread.y][i])
                c += mF(inp[this.thread.x][i], inp[this.thread.x][i])
            }

            return a / (Math.pow(b, 0.5) * Math.pow(c, 0.5));

        }).setOutput([52000, 52000])


Можно ли это оптимизировать?

В среднем на расчет всех косинусовых значений от одного вектора ко всем остальным уходит 7мс (без GPU 36 мс), что составляет почти 6 минут для расчета всей матрицы. Я в JS профан и, скорее всего, не вижу очевидных способов оптимизации кода для ускорения его работы.
Так же скрипт благополучно умирает при расчете всей огромной матрицы, приходится считать кусочками по 1000 векторов.

Спасибо
  • Вопрос задан
  • 1586 просмотров
Решения вопроса 1
@Eldrich Автор вопроса
Нашел решение, объединив возможности библиотеки и совет @Taraflex

Код выглядит так:
var kernelMatrixSize = 4096

        const gpu = new GPU({
            mode: 'gpu'
        });

        const f3 = gpu.createKernel(function(inp1, inp2) {

                var a = 0
                var b = 0
                var c = 0
                for (var i = 0; i < 300; i++) {
                    a += inp1[this.thread.y][i] * inp2[this.thread.x][i]
                    b += Math.pow(inp1[this.thread.y][i], 2)
                    c += Math.pow(inp2[this.thread.x][i], 2)
                }

                return Math.floor(a / (Math.sqrt(b * c)) * 1000000) / 1000000;

            }).setOutput([kernelMatrixSize, kernelMatrixSize])
            .setOutputToTexture(true);
x = getAllDistanceBetween(vectorsData)
y = x[0].toArray(gpu)


Если стоит метка setOutputToTexture, то вычисление матрицы 4096х4096 длится 224 мс, преобразование текстуры в массив занимает примерно 3 секунды. Высчитываем только половину матрицы, так как расстояние от 0 вектора до 1 = расстоянию от 1 к 0. Итого вычисление всех необходимых ячеек займет чуть меньше 60 секунд, вместо 6 минут ранее.

Всем спасибо за помощь.
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
Так же скрипт благополучно умирает при расчете всей огромной матрицы, приходится считать кусочками по 1000 векторов.

Может имеет смыл отправлять эти куски в SharedWorker
Ответ написан
Комментировать
string15
@string15
Учусь верстать руками
Можно попробовать WebAssembly
Ответ написан
Комментировать
dom1n1k
@dom1n1k
Никогда не имел дел с GPUJS, но то что бросается в глаза из общих сооражений - кешировать индексы и обязательно заменить pow 0.5 на sqrt (причем на один, а не два!).
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы