#javascript #node.js #performance
#javascript #node.js #Производительность
Вопрос:
У меня есть очень большая строка в кодировке base64, которую необходимо прочитать в массив байтов (Uint8), а затем разделить этот массив байтов на фрагменты указанного размера. base64 кодирует эти фрагменты отдельно. Использование приведенной ниже функции работает, но вызов .slice или .toString увеличивает память в куче при каждом вызове, потому что (я полагаю) он создает копию буфера. Для особенно больших строк, закодированных в base64Encoded, приложению не хватит места в куче. Что можно сделать, чтобы разделить это на указанные размеры и кодировать их в base64 без нехватки памяти?
const process = function (reallyLargeBase64EncodedString, splitSize){
var theBuffer = Buffer.from(reallyLargeBase64EncodedString, 'base64');
//var tempBuffer = new Buffer(splitSize);
for (var start = 0; start < theBuffer.length; start = splitSize) {
//for(var z = 0; z < splitSize; z ){
//tempBuffer.writeUInt8( theBuffer[start z],z);
//}
//var base64EncodedVal = tempBuffer.toString('base64');
//var base64EncodedVal = theBuffer.buffer.toString('base64', start, start splitSize);
var base64EncodedVal = theBuffer.slice(start,start splitSize).toString('base64');
//do stuff with the base64 encoded value
}
};
Комментарии:
1. Нет,
slice
не копирует память .toString
делает. Что именно «материал», который вы делаете со строками?2. Прямо сейчас вставляем их в базу данных. Хммм, проблема в строке toString, возможно, будет лучше изменить эту строку со строки на большой двоичный объект и просто вставить массив байтов напрямую.
3. Является ли вставка базы данных асинхронной? Возможно, вы захотите сделать его последовательным, чтобы не все строки создавались в памяти одновременно.
4. На самом деле я выполнял вставку с этой последней строкой, просто взял код базы данных для примера.
5. Если у вас есть «очень большой буфер» в Node. JS, тогда ты делаешь что-то не так.
Ответ №1:
Я бы рекомендовал использовать потоковый интерфейс узла для работы с чем-то таким большим. Если ваша строка в кодировке base64 поступает из файла или сетевого запроса, вы можете передавать ее непосредственно из входных данных в поток декодирования base64, например base64-stream .
Чтобы разделить данные и перекодировать каждый фрагмент, вам нужно будет написать свой собственный поток преобразования (поток, который проходит между входом и выходом). Это будет выглядеть примерно так
// NOTE: the following code has been tested in node 6.
// since it relies on the new Buffer api, it must be run in 5.10
var Transform = require('stream').Transform;
class ChunkEncode extends Transform {
constructor(options){
super(options);
this.splitSize = options.splitSize;
this.buffer = Buffer.alloc(0);
}
_transform(chunk, encoding, cb){
// chunk is a Buffer;
this.buffer = Buffer.concat([this.buffer, chunk]);
while (this.buffer.length > this.splitSize){
let chunk = this.buffer.slice(0, this.splitSize);
// Encode and write back to the stream.
this.push(chunk.toString('base64'))
// throw in a newline for visibility.
this.push('n');
// chop off `splitSize` from the start of our buffer.
this.buffer = this.buffer.slice(this.splitSize);
}
}
}
Тогда вы должны быть в состоянии сделать что-то вроде
var fs = require('fs');
var base64 = require('base64-stream');
fs.createReadStream('./long-base64-string')
.pipe(base64.decode())
.pipe(new ChunkEncode({splitSize : 128}))
.pipe(process.stdout)
это приведет к выходу из системы, но вы можете так же легко выполнить запись в файл или сетевой поток. Если вам нужно дополнительно манипулировать данными, вы можете создать поток записи, который позволит вам что-то делать с каждым фрагментом данных по мере их поступления.
Комментарии:
1. Это потрясающе, и я действительно хочу отметить это как ответ, но я решил проблему совершенно по-другому, и единственная причина, по которой ваш ответ не был тем, потому что я не опубликовал ВЕСЬ код, и я прошу прощения за это. Настоящая проблема в том, что я n00b, когда дело доходит до асинхронной парадигмы. Этот код был вызван из результата запроса, результаты которого были отправлены для вызова API, а затем кода, который я опубликовал, и вставки для строки b64. Начальный вызов базы данных еще не завершился, и все, что я там делал, было выброшено в кучу, пока оно не могло быть обработано.
2. Не беспокойтесь об этом. Трудно сказать, какие фрагменты кода будут важны в подобном вопросе. Тем более, что причина, по которой вы задали вопрос, заключается в том, что у вас не было ответа 🙂 Я думаю, что процедура в этом случае заключается в том, чтобы написать свой собственный ответ. (в значительной степени этот комментарий выше) и пометьте его как правильный.
3. Я тоже проверил ваш ответ, чтобы посмотреть, сохранит ли он размер кучи, и это произошло.. недостаточно, чтобы остановить переполнение кучи, но цикл for продвинулся более чем в два раза, прежде чем его вырвало.
4.
_transform
Метод не вызывает обратный вызов и_flush
должен быть также реализован для отправки оставшихся элементов в буфер в конце потока.