Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Ускорите код? (https://javascript.ru/forum/misc/80786-uskorite-kod.html)

Shitbox2 01.08.2020 08:56

Ускорите код?
 
Уважаемые знатоки, возможно ли ускорить функцию groupBy хотя бы в 1,5 раза (для node.js)? :)
/**
 * Group items by field
 *
 * items = [
 *  {id: 1, name: 'A', val: 1, smth: true},
 *  {id: 2, name: 'B', val: 2, ...},
 *  {id: 3, name: 'A', val: 3, ...},
 *  {id: 4, name: 'B', val: 4, ...},
 *  {id: 5, name: 'A', val: 5, ...}
 * ]
 *
 * groupBy(items, 'name', ['val', ...]) =
 *   [
 *     {
 *       groupField: 'name',
 *       groupValue: 'A',
 *
 *       // Items of the group
 *       items: [{id: 1, name: 'A', val: 1, , smth: true}, {id: 3, name: 'A', val: 3}, {id: 5, name: 'A', val: 5}],
 *
 *       // Values of first item
 *       id: 1,
 *       smth: true,
 *       ...
 *
 *       // Summed values
 *       val: 9,
 *       ...
 *     }, {
 *       groupField: 'name',
 *       groupValue: 'B',
 *       items: [{id: 2, name: 'B', val: 2}, {id: 4, name: 'B', val: 4}],
 *       id: 2,
 *       val: 6,
 *       ...
 *     }
 *   ]
 *
 * @param items - Array<any> - items array
 * @param field - string - field name
 * @param sumFields - Array<string> - array with names of fields which should be summed
 * @returns Array<any>
 */
function groupBy(items, field, sumFields = []) {
    const groupsMap = {};

    for (let i = 0; i < items.length; i++) {
        const item = items[i];
        const groupValue = item[field];
        let group = groupsMap[groupValue];

        if (!group) {
            group = {items: [], groupField: field, groupValue: groupValue, ...item};
            groupsMap[groupValue] = group;
        }

        // Add values which should be summed
        for (let j = 0; j < sumFields.length; j++) {
            const fieldName = sumFields[j];
            const sumValue = item[fieldName];

            if (sumValue) {
                group[fieldName] += sumValue;
            }
        }

        group.items.push(item);
    }

    return Object.keys(groupsMap).map(key => groupsMap[key]);
}

function generateData(amount) {
    return Array.from({length: amount}, (v, i) => {
        return {
            id: i,
            group: String.fromCharCode(Math.floor(Math.random() * 25) + 65),
            string: Math.random().toString(36).substring(2),
            number: Math.random() * 100,
            number2: Math.random() * 1000,
            null: null,
            undefined: undefined,
            boolean: true
        }
    })
}

const data = generateData(100000)
console.time('Groupping')
grouped = groupBy(data, 'group', ['number', 'number2'])
console.timeEnd('Groupping')

Nexus 01.08.2020 12:11

Shitbox2, что у вас делает 70-я строчка?

Upd. еще для ускорения можно попробовать перебирать массив сразу с двух сторон.

Shitbox2 01.08.2020 14:23

Цитата:

Сообщение от Nexus (Сообщение 527480)
что у вас делает 70-я строчка?

Делает из мапы массив. На производительности практически на сказывается)

Самая тяжелая операция тут - let group = groupsMap[groupValue]

Цитата:

Сообщение от Nexus (Сообщение 527480)
Upd. еще для ускорения можно попробовать перебирать массив сразу с двух сторон.

Перебор с обоих сторон тут ничего не даст. Или были реальные замеры?

Nexus 01.08.2020 14:31

Цитата:

Сообщение от Shitbox2
Делает из мапы массив.

Может тогда лучше Object.values использовать?

Цитата:

Сообщение от Shitbox2
Самая тяжелая операция тут - let group = groupsMap[groupValue]

Вот уж вряд ли.

Цитата:

Сообщение от Shitbox2
Перебор с обоих сторон тут ничего не даст.

Почему вы так считаете? Кол-во итераций уменьшится вдвое, скорость соответственно должна увеличиться.

И в своем коде вы замеряете не только скорость работы вашей функции группировки, но и скорость генерации ваших данных.

MallSerg 01.08.2020 16:09

>> возможно ли ускорить функцию groupBy
Врятли.
По сути тут идет простая итерация всего объекта с простым условным ветвлением для фильтрации значений по установленным признакам.
Можно развернуть цикл делая по двадцать итераций за проход но с такими оптимизациями интерпретатор и сам справляется.

Тут с логикой проблема, управление данными осуществляется в приложении алгоритмами собранными топориком на коленке.
Задачи связанные с группировкой, хранением, сортировкой, индексацией и выборкой данных лучше решать с помощью баз данных.

MallSerg 01.08.2020 16:44

Цитата:

Вот уж вряд ли.
Это ссылка на внешнюю переменную что не позволяет оптимизатору автоматически развернуть цикл.
Более того этот объект изменяется на текущей итерации цикла т.е. пока не закончится текущая итерация и объект не будет изменен следующую/параллельную итерацию интерпретатор выполнять не может так как состояние объекта еще не определенно что работает как брошенный якорь на гоночном автомобиле.
Основной способ оптимизации выполнения это избавление от побочных эффектов при выполнении блока кода или вынос их в отдельный блок кода.
А строки 50, 67 как раз и есть эти побочные эффекты.
Но данные во внешнем изменяемом объекте не пересекаются по этому если в ручную развернуть цикл то это должно дать прибавку к скорости. Хотя оптимизатор наверно и сам может просчитать что данные не пересекаются и ожидать изменения объекта не обязательно.

MallSerg 01.08.2020 16:49

размотка цикла
Побочный_эффект_(программи ование)

Alexandroppolus 01.08.2020 18:40

Попробовать использовать Map вместо объекта.
Группы сразу пушить в массив, который вернуть вместо выражения на строке 70.

Читерство: суммируемые поля оформить как проперти, можно с мемоизацией.

voraa 01.08.2020 19:21

Цитата:

Сообщение от MallSerg
Это ссылка на внешнюю переменную что не позволяет оптимизатору автоматически развернуть цикл.

Это Javascript. Это Node.js Это V8.
Он ничего не разворачивает.

MallSerg 01.08.2020 20:37

Цитата:

Сообщение от voraa
Это Javascript. Это Node.js Это V8.
Он ничего не разворачивает.

Да неужели? В самом деле? ))

Прямая ссылка на исходник где происходит оптимизация турбофаном в V8
https://github.com/v8/v8/blob/4b9b23...eline.cc#L1064

Если вкратце то турбофан пытается байткод виртуальной машины V8 транспилировать в машинный код текущего процессора.
Причем с такой PGO оптимизацией что компиляторы C/C++ пускают слюни от зависти.
Ничего необычного пытается заинлайнить методы и развернуть цыклы где это удается ну и еще дефрагментирует расположение данных в памяти.


Часовой пояс GMT +3, время: 23:57.