NodeJS, обещания и производительность

Мой вопрос касается производительности в моем приложении 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 не работает оптимально - или может быть просто дизайн кода снова...

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

узел --max-old-space-size="50000" server.js

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

Большое спасибо всем вам!

1 ответ

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

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

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

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

Рассмотрим этот пример:

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 МБ памяти и закончил.

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

Другие вопросы по тегам