NodeJS, Promises и производительность

Мой вопрос о производительности в моем приложении NodeJS...

Если моя программа запускает 12 итераций по 1.250.000 каждый = 15.000.000 итераций вместе - для Amazon требуется выделенные серверы в следующий раз:

r3.large: 2 vCPU, 6.5 ECU, 15 ГБ памяти → 123 минуты

4.8xlarge: 36 vCPU, 132 ECU, 60 ГБ памяти → 102 минуты

У меня есть код, похожий на код ниже...

start();

start(){

  for(var i=0; i<12; i++){

      function2();    // Iterates over a collection - which contains data split up in intervals - by date intervals. This function is actually also recursive - due to the fact - that is run through the data many time (MAX 50-100 times) - due to different intervals sizes...
    }
}

function2(){

  return new Promise{

    for(var i=0; i<1.250.000; i++){       
         return new Promise{      
            function3();      // This function simple iterate through all possible combinations - and call function3 - with all given values/combinations
         }
      }   
   } 
}


function3(){
   return new Promise{ // This function simple make some calculations based on the given values/combination - and then return the result to function2 - which in the end - decides which result/combination was the best...
}}

Это равно 0,411 миллисекунды /441 микросекунды итерации!

Когда я смотрю на производительность и использование памяти на панели задач... процессор не работает на 100% - но больше похоже на 50%... все время? Использование памяти начинается очень низко - но KEEPS растет в ГБ - каждую минуту, пока процесс не будет выполнен - ​​НО (выделенная) память сначала выдается, когда я нажимаю CTRL + C в Windows CMD... так что это похоже на сборку мусора NodeJS не работает оптимально - или может быть его простой дизайн кода снова...

Когда я выполняю приложение, я использую память, например:

node --max-old-space-size = "50000" server.js

ПОЖАЛУЙСТА, расскажите мне все, что я могу сделать, чтобы сделать мою программу FASTER!

Спасибо всем - так много!

Ответ 1

Это не то, что сборщик мусора работает не оптимально, но что он вообще не работает - вы не даете ему никаких шансов.

При разработке tco module, который tail call оптимизация в Node я заметил странную вещь. Казалось, это утечка памяти, и я не знал почему. Оказалось, что из-за немногих console.log() звонки в разных местах, которые я использовал для тестирования, чтобы увидеть, что происходит, потому что, увидев результат рекурсивного вызова, уровень миллионов в миллионных глубинах занял некоторое время, поэтому я хотел увидеть что-то, пока он это делал.

Ваш пример очень похож на этот.

Помните, что Node является однопоточным. Когда ваши вычисления выполняются, ничто другое не может - включая GC. Ваш код полностью синхронный и блокирующий - хотя он генерирует миллионы promises блокирующим образом. Он блокируется, потому что он никогда не достигает цикла события.

Рассмотрим следующий пример:

var a = 0, b = 10000000;

function numbers() {
  while (a < b) {
    console.log("Number " + a++);
  }
}

numbers();

Это довольно просто - вы хотите напечатать 10 миллионов номеров. Но когда вы запускаете его, он ведет себя очень странно - например, он печатает числа до некоторой точки, а затем останавливается на несколько секунд, затем он продолжает идти или может начать сбой, если вы используете swap, или, может быть, дает вам эту ошибку, Я только что получил номер 8486:

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory
Aborted

Что здесь происходит, так это то, что основной поток блокируется в синхронном цикле, где он продолжает создавать объекты, но GC не имеет возможности их выпуска.

Для таких длительных задач вам нужно разделить работу и время от времени входить в цикл событий.

Вот как вы можете исправить эту проблему:

var a = 0, b = 10000000;

function numbers() {
  var i = 0;
  while (a < b && i++ < 100) {
    console.log("Number " + a++);
  }
  if (a < b) setImmediate(numbers);
}

numbers();

Он делает то же самое - он печатает числа от a до b, но в пучках по 100, а затем он планирует себя продолжать в конце цикла событий.

Вывод $(which time) -v node numbers1.js 2>&1 | egrep 'Maximum resident|FATAL'

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory
    Maximum resident set size (kbytes): 1495968

Он использовал 1,5 ГБ памяти и разбился.

Вывод $(which time) -v node numbers2.js 2>&1 | egrep 'Maximum resident|FATAL'

    Maximum resident set size (kbytes): 56404

Он использовал 56 МБ памяти и закончил.

См. также эти ответы: