我在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")
}
}
答案 0 :(得分:1)
在nodejs代码实现中可以使用哪些因素来优化内存使用?
以下是要考虑的一些事情:
在处理或输出数据之前,不要缓存任何多余的输入数据。
请尝试避免复制数据。尽可能使用适当的数据。请记住,所有字符串操作都会创建一个新字符串,该字符串很可能是原始数据的副本。而且,许多数组操作,例如.map()
,.filter()
等……都会创建原始数组的新副本。
请记住,垃圾回收是延迟的,通常在空闲时间完成。因此,例如,在循环中修改字符串可能会创建许多临时对象,这些临时对象必须同时存在,即使在循环完成后大部分或全部将被垃圾回收。这会导致峰值内存使用率降低。
缓冲
我注意到的第一件事是,在处理任何输入文件之前,您已将整个输入文件读入内存。立即为大型输入文件,您将使用大量内存。取而代之的是,您要做的是读取足够的块以获取下一个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倍或更糟。