Dados de nó e binários

Ao trabalhar com dados binários brutos, o NodeJS possui um Bufferobjeto 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 Bufferobjetos 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 Buffere 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 Bufferobjeto 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 DataViewdobro do tempo do Bufferobjeto. Portanto, parece que, na criação e no carregamento, os Typed Arrays estão praticamente no mesmo nível dos Bufferobjetos, mas uma vez que você começa a manipular os dados, fica muito mais lento. Portanto, os Bufferobjetos 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?