如何提高Node.js代码中的内存使用率?

时间:2019-11-25 18:39:42

标签: node.js data-structures

我在hackerearth尝试了一个代码:https://www.hackerearth.com/practice/data-structures/stacks/basics-of-stacks/practice-problems/algorithm/fight-for-laddus/description/

速度似乎不错,但是内存使用量超出256mb限制近2.8倍。 在java和python中,内存少5倍,但是时间几乎是原来的两倍。

在nodejs代码实现中可以使用哪些因素来优化内存使用?

这是nodejs的实现:

// Sample code to perform I/O:

process.stdin.resume();
process.stdin.setEncoding("utf-8");
var stdin_input = "";

process.stdin.on("data", function (input) {
    stdin_input += input;                               // Reading input from STDIN
});

process.stdin.on("end", function () {
    main(stdin_input);
});

function main(input) {

    let arr = input.split("\n");
    let testCases = parseInt(arr[0], 10);
    arr.splice(0,1);
    finalStr = "";
    while(testCases > 0){
        let inputArray = (arr[arr.length - testCases*2 + 1]).split(" ");
        let inputArrayLength = inputArray.length;
        testCases = testCases - 1;
        frequencyObject = { };
        for(let i = 0; i < inputArrayLength; ++i) {
            if(!frequencyObject[inputArray[i]])
            {
                frequencyObject[inputArray[i]] = 0;
            }
            ++frequencyObject[inputArray[i]];
        }

        let finalArray = [];
        finalArray[inputArrayLength-1] = -1;
        let stack = [];
        stack.push(inputArrayLength-1);
        for(let i = inputArrayLength-2;  i>=0; i--)
        {
            let stackLength = stack.length;
            while(stackLength > 0  &&  frequencyObject[inputArray[stack[stackLength-1]]] <= frequencyObject[inputArray[i]])
            {
                stack.pop();
                stackLength--;
            }
            if (stackLength > 0) {
                finalArray[i] = inputArray[stack[stackLength-1]];
            }  else {
                finalArray[i] = -1;
            }
            stack.push(i);
        }
        console.log(finalArray.join(" ") + "\n")
    }

}

enter image description here

1 个答案:

答案 0 :(得分:1)

  

在nodejs代码实现中可以使用哪些因素来优化内存使用?

以下是要考虑的一些事情:

  1. 在处理或输出数据之前,不要缓存任何多余的输入数据。

  2. 请尝试避免复制数据。尽可能使用适当的数据。请记住,所有字符串操作都会创建一个新字符串,该字符串很可能是原始数据的副本。而且,许多数组操作,例如.map().filter()等……都会创建原始数组的新副本。

  3. 请记住,垃圾回收是延迟的,通常在空闲时间完成。因此,例如,在循环中修改字符串可能会创建许多临时对象,这些临时对象必须同时存在,即使在循环完成后大部分或全部将被垃圾回收。这会导致峰值内存使用率降低。

缓冲

我注意到的第一件事是,在处理任何输入文件之前,您已将整个输入文件读入内存。立即为大型输入文件,您将使用大量内存。取而代之的是,您要做的是读取足够的块以获取下一个testCase,然后对其进行处理。

仅供参考,这种增量式的读取/处理将使代码的编写变得更加复杂(我自己编写了一个实现),因为您必须处理部分读取的行,但是这会占用大量内存,这就是您要做的要求。

数据副本

将整个输入文件读入内存后,您可以使用以下命令立即将其全部复制:

let arr = input.split("\n");

因此,现在您将输入数据占用的内存量增加了一倍以上。现在,不仅所有输入都只包含一个字符串,您现在仍将所有这些存储在内存中,但是现在您将其分解为数百个其他字符串(每个字符串都有自己的一点开销用于新字符串,当然还有每行的副本)。

修改循环中的字符串

创建称为finalStr的最终结果时,需要一遍又一遍:

finalStr = finalStr + finalArray.join(" ") + "\n"

这将创建成千上万的增量字符串,这些字符串很可能一次都存储在内存中,因为垃圾回收可能要等到循环结束后才能运行。例如,如果您有100行输出,每行100个字符长,所以总输出(不算行终止符)为100 x 100 = 10,000个字符,则像这样循环构建,将创建临时字符串100、200、300、400,... 10,000,这将消耗5000(平均长度)* 100(临时字符串的数量)= 500,000个字符。这是临时字符串对象消耗的总输出大小的 50x

因此,这不仅会创建成吨的增量字符串(每个字符串都比前一个大)(因为您要添加到它上面),而且还会在将所有输出写入标准输出之前在内存中创建整个输出。

相反,在构造每行时,可以将每行递增输出到stdout。这将使最坏的情况下的内存使用量约为输出大小的2倍,而您的内存使用量则为50倍或更糟。