Ao trabalhar com dados binários brutos, o NodeJS possui um Buffer
objeto construído para esse propósito, que é específico do Node. No entanto, o Javascript criou matrizes digitadas que são efetivamente a mesma coisa. Mas você pensaria que, como os Buffer
objetos do Node foram feitos sob medida para essa plataforma, eles seriam otimizados para ela e a estrutura mais abstrata de Typed Arrays (construída para Javascript, não o mecanismo Node / V8 sob ela) seria menos eficaz. Mas são eles? Vamos testar!
Portanto, a questão é: se você tem um conjunto de dados binários em um Array Javascript regular, é mais rápido convertê-lo em um Buffer
e trabalhar com ele ou em um Array Digitado DataView
?
Ambos os processos têm uma maneira de absorver uma matriz de dados existentes como uma opção de inicialização, mas se os compararmos rapidamente:
var crypto = require('crypto');
var rounds = 5000;
var arraySize = 10000;
console.time('Random array generation');
for (var i = 0; i < rounds; i++) {
var buff = getRandomArray();
buff = null;
}
console.timeEnd('Random array generation');
console.time('Create and load Buffer');
for (var i = 0; i < rounds; i++) {
var buff = new Buffer(getRandomArray());
buff = null;
}
console.timeEnd('Create and load Buffer');
console.time('Create and load ArrayBufferView');
for (var i = 0; i < rounds; i++) {
var buff = new Uint8Array(getRandomArray());
buff = null;
}
console.timeEnd('Create and load ArrayBufferView');
function getRandomArray() {
var random = crypto.randomBytes(arraySize);
return Array.prototype.slice.apply(random);
}
Nesta configuração, a “geração de array aleatório” é a sobrecarga adicionada para criar os arrays de dados a serem testados, e acho que os Arrays digitados são muito mais lentos do que os Buffers quando tratados dessa maneira. Alguém poderia simplesmente ir embora e dizer: “Aha, sim, os objetos Buffer são otimizados para uso no Node!” Mas dê uma olhada nisso: Se separarmos a inicialização e o carregamento dos dados, obtemos:
console.time('Create, then load Buffer');
for (var i = 0; i < rounds; i++) {
var data = getRandomArray();
var buff = new Buffer(data.length);
for (var x = 0; x < data.length; x++) {
buff[x] = data[x];
}
data = null;
buff = null;
}
console.timeEnd('Create, then load Buffer');
console.time('Create, then load Buffer (writeUInt8)');
for (var i = 0; i < rounds; i++) {
var data = getRandomArray();
var buff = new Buffer(data.length);
for (var x = 0; x < data.length; x++) {
buff.writeUInt8(data[x], x);
}
data = null;
buff = null;
}
console.timeEnd('Create, then load Buffer (writeUInt8)');
console.time('Create, then load ArrayBufferView');
for (var i = 0; i < rounds; i++) {
var data = getRandomArray();
var i8 = new Uint8Array(data.length);
for (var x = 0; x < data.length; x++) {
i8[x] = data[x];
}
data = null;
i8 = null;
}
console.timeEnd('Create, then load ArrayBufferView');
Em meus testes, carregar um Buffer
objeto por meio do acessador de índice de array é mais rápido do que alimentá-lo com um array, e mais rápido do que usar o método (por um fio de cabelo). No entanto, vejo grandes melhorias no objeto ArrayBufferView, colocando-o de volta no mesmo nível de um objeto Buffer em termos de velocidade de carregamento. Portanto, os objetos Buffer são mais rápidos quando podem ser acessados por índice, mas e se você estiver lidando com valores binários maiores do que 8 bits? Os buffers têm métodos de leitura / gravação para tamanhos de bits maiores, mas como eles se acumulam, visto que acabamos de ver que o método não é tão rápido quanto o acesso ao array?writeUInt8()
writeUInt8()
console.log('32-bit Big-endian numbers:');
console.time('Node Buffer object');
for (var i = 0; i < rounds; i++) {
var data = getRandomArray();
var buff = new Buffer(data.length); // Create
for (var x = 0; x < data.length; x++) {
buff[x] = data[x]; // Load
}
// Manipulate
for (var x = 0; x < buff.length-10; x++) {
buff.writeUInt32BE(buff.readUInt32BE(x+5), x);
}
data = null;
buff = null;
}
console.timeEnd('Node Buffer object');
console.time('DataView');
for (var i = 0; i < rounds; i++) {
var data = getRandomArray();
var i8 = new Uint8Array(data.length); // Create
for (var x = 0; x < data.length; x++) {
i8[x] = data[x]; // Load
}
// Manipulate
var buff = new DataView(i8.buffer);
for (var x = 0; x < buff.byteLength-10; x++) {
buff.setUint32(x, buff.getUint32(x+5), false);
}
data = null;
buff = null;
}
console.timeEnd('DataView');
Esses testes saem de maneira bem diferente para mim em minha estação de trabalho de teste, com o DataView
dobro do tempo do Buffer
objeto. Portanto, parece que, na criação e no carregamento, os Typed Arrays estão praticamente no mesmo nível dos Buffer
objetos, mas uma vez que você começa a manipular os dados, fica muito mais lento. Portanto, os Buffer
objetos gerais são melhores para uso ao trabalhar com dados binários no ambiente NodeJS, a menos que alguém possa encontrar uma falha em meus casos de teste aqui. Ou existe uma maneira de contornar o gargalo na manipulação de dados que encontrei?