Параллельная обработка огромных объемов данных, поровну разделенных на доступные ядра - PullRequest
0 голосов
/ 18 июня 2020

История в том, что у меня довольно тяжелая функция, которая принимает 4 параметра, все они процентные. Я пытаюсь получить те 4 процентных значения, которые дают наилучший возможный результат. Каждый параметр пробуется от 0.00 до 1.00 с шагом 0.01, то есть 100 вариантов для каждого. Поскольку у меня их 4, это означает 100 000 000 возможностей.

Я подсчитал, что с моим бедным Macbook Pro потребуется примерно 14 часов для расчета результата. Аренда 96-ядерной ВМ с огромной памятью казалась очевидной, но скорость вычислений была примерно такой же. После некоторого исследования я понял, что не имеет значения, с каким количеством ядер я рассчитываю, поскольку Node все равно будет использовать одно (неплохо понимать предысторию, а не просто писать код, верно).

From исследование, с которым я познакомился с кластерами , где несколько ядер можно использовать для параллельной работы, что я все еще пытаюсь понять, как это будет работать, но моя идея начать с синхронного стиля, а затем разделить объедините комбинации в numberOfCpus частей и отдайте каждому ядру часть работы, а затем, когда все закончится (например, Promise.all()), go вернитесь в синхронный режим и сделайте все что угодно с массивом результатов.

Единственное, что меня беспокоит, - это, вероятно, кластеризация идеально подходит для «клонирования серверных API-интерфейсов», где они независимы друг от друга, но в моем случае они как бы взаимосвязаны и будут делать разные вещи.

Однако, это лишь быстрый пример того, о чем я думаю:

import { heavyProcessingFunction } from '..';
import cluster from 'cluster';
import { cpus } from 'os';

export handler = async () => {
  const percs = Array.from(Array(100), (_, i) => (i + 1) / 100);
  const combinations = (
    percs.map((a) =>
      percs.map((b) =>
        percs.map((c) =>
          percs.map((d) => [a, b, c, d]),
        ),
      ),
    )
    .flat(3)
  );
  const numberOfCpus = cpus().length;
  const elementsPerPart = Math.floor(combinations.length / numberOfCpus);

  const chunks = [...Array(numberOfCpus)].map((e, i) => {
      if (i === numberOfCpus - 1) {
          return combinations;
      }
      return combinations.splice(0, elementsPerPart);
  });

  //some magic with clusters to run each element in the array below in parallel
  const resultOfEachCore = await Promise.all(
    chunks.map(combinations => (
      clusterMagic(
        heavyProcessingFunction(...combinations)
      )
    )
  );

  //after finished, process them
  resultOfEachCore//...
}

1 Ответ

0 голосов
/ 18 июня 2020

На самом деле речь не идет о многопоточности, но это может ускорить вычисление вашего решения.

Я посмотрел на optimisation- js, и, похоже, он не требует каких-либо специальных свойств для используемой функции. Поскольку вы ищете максимум, возможно, вы захотите свести на нет вывод HeavyProcessingFunction, прежде чем передавать его в минимизатор. При 10000 итерациях, как написано ниже, выполнение занимает меньше секунды.

const O = window.optimjs;

// An arbitrary function. X is an array of 4 numbers, because `space` has 4 dimentsions
function heavyProcessingFunction(X) {
  const dotProduct = X.map(xi => xi * xi).reduce((acc, xi) => acc + xi);
  return -dotProduct; // algorithm minimizes instead of maximizing
}

const range = new O.Real(0, 1);
const space = new O.Space([range, range, range, range]);
const numIterations = 10000;

var solution = O.rs_minimize(heavyProcessingFunction, space, numIterations);
console.log("Best points:", solution.best_x);
console.log("Value at best points", solution.best_y);
<script src="https://unpkg.com/optimization-js@latest/dist/optimization.js"></script>
...