Executando verificação de segurança...
32

[DESAFIO] NodeJS mais rápido que o Bun v1.0.1

Uma nova runtime de Javascript foi lançada esse mês o Bun e provavelmente tu já sabe. Eu sempre testo performance usando algoritmo basico e assim como fiz pro Mojo vs. Python escolhi fazer Fibonacci.

Eis que o Bun ficou MUITO atras do NodeJS: Video nesse Tweet.

Me ajude a descobrir onde está a perda de performance do Bun.

console.time("Execution Time");
const n = 1000000
let a = BigInt(0), b = BigInt(1); // fixed
for (let i = BigInt(0); i < n; i++) {
  [a, b] = [b, a + b];
}
console.log("fib: " + a);
console.timeEnd("Execution Time");

Adicionando misterio a essa desafio:

Se fizer o loop a seguir sem a matematica do Fibonacci o Bun desempenha melhor que o NodeJS.

console.time("Execution Time");
const n = 1000000
for (let i = BigInt(0); i < n; i++) {
    console.log("Item: " + i);
}
console.timeEnd("Execution Time");

Ambiente que rodei o teste
MacOS

  • Apple Chip M1
  • Apple Chip M1 Max

Nota:
Obviamente rodei o mesmo codigo em ambos e não usei nenhuma biblioteca externa apenas o raw Javacript que o DHH tanto ama.

Carregando publicação patrocinada...
26

Fiz alguns testes aqui, só fiz uma pequena modificação no código, inicializando o n com um BigInt também:

let n = 1000000n; // sufixo "n" faz com que seja BigInt
let a = BigInt(0), b = BigInt(1);
for (let i = BigInt(0); i < n; i++) {
  [a, b] = [b, a + b];
}
console.log(a);

Testei na minha máquina (Ubuntu 22.04.3, processador 11th Gen Intel® Core™ i5-1145G7 2.60GHz, 8 núcleos), primeiro com o teste mais básico, usando o comando time do Linux. Ou seja, time bun run arquivo.js e time node arquivo.js. A versão do Node é v18.17.1, e do Bun é 1.0.1. Os resultados estão abaixo (lembrando que em outros hardwares os tempos poderão ser diferentes, obviamente), só retirei a saída do console.log para não deixar poluído.

Node:

real    0m10,368s
user    0m8,712s
sys     0m0,037s

Bun:

real    0m9,915s
user    0m9,782s
sys     0m1,833s

O que importa é a primeira linha ("real", o tempo que efetivamente se passou entre eu teclar ENTER e o comando terminar).

Só de curiosidade, "user" é o tempo de CPU em user mode (fora do kernel), e "sys" é o tempo de CPU dentro do kernel (em chamadas de sistema, por exemplo). Mas estes tempos são computados considerando todos os núcleos, então se a máquina tem mais de um, a soma de "user" e "sys" pode ser maior que "real", conforme explicado aqui.

Enfim, no meu caso o Node demorou um pouco mais, mas dado o tempo total de ambos (10,3 segundos versus 9,9 segundos), não acho que foi uma diferença tão significativa (cerca de 4% a mais).


Mas tem um detalhe, operações de I/O (como o console.log) costumam ser caras e elas geralmente acabam mascarando o resultado (neste caso nem tanto porque só tem uma, mas enfim). Então removi o console.log para ver apenas o tempo do loop e rodei de novo.

Node:

real    0m8,615s
user    0m8,577s
sys     0m0,033s

Bun:

real    0m9,329s
user    0m9,424s
sys     0m1,710s

Agora o Bun foi ligeiramente mais lento, mas novamente, não acho que a diferença é tão significativa.

Testei mais algumas vezes, e às vezes o Node era mais rápido, às vezes era mais lento, mas sempre com diferenças pequenas. Nada tão gritante quanto o que vc encontrou.


Uma maneira melhor de testar

O problema de fazer um teste isolado é justamente esse: vc roda uma vez um pequeno trecho de código e já acha que é o suficiente, mas conforme já visto aqui, isso pode levar a conclusões precipitadas.

Mesmo que rode várias vezes, podem ter outros fatores que influenciam, como outros processos rodando na mesma máquina (ainda mais se tiver I/O e outras operações bloqueantes), e até mesmo a própria inicialização do runtime (tanto o Node quanto o Bun precisam de uma etapa de inicialização antes de começar a rodar o código propriamente dito).

Sendo assim, uma forma melhor de testar seria usar uma lib específica que desconsidera esses fatores externos. Eu usei o Benchmark.js, o código ficou assim:

var Benchmark = require('benchmark');
var suite = new Benchmark.Suite;

suite.add('test', function () {
    let n = 1000000n;
    let a = BigInt(0), b = BigInt(1);
    for (let i = BigInt(0); i < n; i++) {
      [a, b] = [b, a + b];
    }
}).on('cycle', function (event) {
    console.log(String(event.target));
}).run({
    'async': true
});

Obs: o propósito do Benchmark.js na verdade é comparar dois códigos diferentes. Por exemplo, chamo várias vezes add com algoritmos diferentes, e no final ele mostra qual é mais rápido. Mas aqui eu coloquei apenas um, e rodei este teste no Node e depois no Bun, pois ele também mostra a quantidade de operações por segundo que conseguiu executar. E é exatamente isso que eu quero comparar, pois dá para ter uma ideia melhor do desempenho de cada um.

Resultados com Node:

test x 0.12 ops/sec ±0.24% (5 runs sampled)

E com Bun:

test x 0.11 ops/sec ±0.90% (5 runs sampled)

Ou seja, 0,12 operações por segundo versus 0,11 operações por segundo. Em outras palavras, no Node demoraria cerca de 8,3 segundos para rodar o código uma vez, e no Bun, cerca de 9 segundos. Mais uma vez, uma diferença bem pequena, nada da discrepância que vc encontrou.

Rodei mais algumas vezes e os resultados giraram em torno disso, com pouca variação, e às vezes um era melhor, às vezes o outro - o que mostra que mesmo uma lib que desconsidera fatores externos não consegue eliminar 100% deles.


Quanto ao segundo caso (o console.log dentro do loop), realmente o Node foi pior em todos os casos. Primeiro com time, segue abaixo.

Bun:

real    0m2,648s
user    0m0,656s
sys     0m1,416s

Node:

real    0m5,264s
user    0m4,276s
sys     0m0,884s

E com o Benchmark.js:

Bun:

test x 0.38 ops/sec ±2.40% (5 runs sampled)

Node:

test x 0.19 ops/sec ±7.30% (5 runs sampled)

Ou seja, o Bun foi cerca de duas vezes mais rápido, uma diferença maior que no primeiro código.

Pesquisei um pouco a respeito e encontrei isso, que indica que a implementação do console.log no Node acaba deixando-o pior para este caso específico.


Conclusões (ou não)

No fim das contas, é difícil tirar conclusões definitivas sobre qual deles sempre será mais rápido. Isso depende de tantos fatores que o melhor a fazer é testar em uma situação mais próxima possível do caso concreto: teste o seu sistema, com seu código e seus casos de uso, e veja se faz diferença. Testar rodando um código qualquer uma ou poucas vezes é o teste mais ingênuo e propenso a erros que vc pode fazer (e tirar "verdades" disso é pior ainda). Testar a situação real costuma ser mais efetivo, pois aí vc está considerando o seu contexto específico em vez de só seguir a moda. Mais ainda, como vimos acima, em um caso particular pode fazer mais diferença que em outros, então o melhor é testar com o código que vc efetivamente vai usar.

Se procurar por benchmarks que comparam os dois, vai encontrar vários diferentes, e é importante ver qual código foi usado para testar, já que isso pode fazer diferença. E claro que também precisa considerar outros fatores: se usar A ou B vai facilitar o dia-a-dia da sua equipe, se é estável, se aguenta o tranco, etc etc etc. Vc pode inclusive concluir que não faz diferença, e não tem problema, pois o que importa é que seja uma decisão embasada, em vez de só puro achismo ou "ouvi dizer que é melhor".

5

Exato! Foi um typo porque eu reescrevi o Fibonacci enquanto postava aqui :)
b = BigInt(1)

PRIMEIRAMENTE: MUITO MASSA A TUA ANALISE!
Eu vou postar um video amanhã sobre e vou colocar o link do TabNews nele porque acredito que até amanhã conseguimos chegar na conclusão do porque o NodeJS performa muito mais rapido que o Bun (todas as vezes aqui) no meu ambiente com um MacOS M1 Max. Não usei nenhuma biblioteca, apenas usei de fato o console.log e um console.time.

Eu vou rodar o Benchmark.Suite; aqui e postar como que ficou no M1.

3

O resultado aqui no M1 Max foi bem diferente.

rinha % bun execute.js
test x 3.80 ops/sec ±5.63% (14 runs sampled)

rinha % node execute.js      
test x 12.14 ops/sec ±3.39% (35 runs sampled)

Rodei multiplas vezes em todas o bun ficava em 3-4 ops/sec enquanto o Node ficava em 11-12 ops/sec, confirmando que no meu M1 Max rodando o mesmo teste que você o Node performou 3x-4x mais rapido que bun.

Seria interessante alguem com Apple Chip M1 fazer o mesmo testes, e talvez alguem com MacOs Intel Chip.

3

Eu tenho um MacBook antigo (2012), processador 2.6 GHz Quad-Core Intel Core i7, e refiz o teste nele com as mesmas versões (Node 18.17.1 e Bun 1.0.1).

Agora a diferença foi bem maior no código que calcula Fibonacci. Primeiro com time:

Node:

real    0m9.818s
user    0m9.412s
sys     0m0.443s

Bun:

real    0m33.275s
user    0m33.062s
sys     0m3.324s

E com o Benchmark.js:

Node:

test x 0.14 ops/sec ±1.67% (5 runs sampled)

Bun:

test x 0.03 ops/sec ±0.36% (5 runs sampled)

Já com o segundo código (console.log dentro do loop), a situação se inverte e o Bun se mostra mais rápido. Primeiro com time:

Node:

real    0m12.736s
user    0m10.322s
sys     0m1.732s

Bun:

real    0m5.140s
user    0m2.289s
sys     0m1.335s

E com o Benchmark.js:

Node:

test x 0.09 ops/sec ±0.15% (5 runs sampled)

Bun:

test x 0.21 ops/sec ±0.15% (5 runs sampled)

Provavelmente por causa do que já mencionei, de que a implementação do console.log no Node piora bastante a performance. No primeiro código não causa problema porque é só uma chamada no final e o grosso do trabalho é no cálculo, mas no segundo já faz diferença.


Curiosamente, se não usar BigInt, aí muda de novo:

// diminuí o n para não estourar o valor máximo de Number
let n = 1000;
let a = 0, b = 1;
for (let i = 0; i < n; i++) {
    [a, b] = [b, a + b];
}

Usando este código, o Bun foi muito mais rápido que o Node, tanto no Mac quanto no Linux (em média cerca de 10 vezes mais rápido).

1

só que usando esse codigo tu nao chega em n de 100.000 imagina 1.000.000 haha

BigInt parece ser uma limitação do bun atual, sobre o console.log se nao me engano ele vai direto pra API nativa no V8, provavelmente aqui temos uma diferença do Zig no bun?

2

Bom, a ideia era eliminar o uso de BigInt pra ver se mudava alguma coisa, então o jeito foi diminuir o valor.

Mas se somente o BigInt fosse o problema, então daria diferença no Linux também, mas aqui deu "empate técnico". Talvez seja a combinação BigInt + Mac que cause essa perda de desempenho, fica aí a questão pra uma futura investigação :-)

Também pensei se a desestruturação faz alguma diferença, fica aí outra sugestão de teste também (usar atribuições simples em vez de [a, b] = [b, a + b]).

2

Bom, fiz o teste comparando os algoritmos com e sem desestruturação, e também com e sem BigInt:

var Benchmark = require('benchmark');
var suite = new Benchmark.Suite;

suite
.add('desestruturação', function () {
    const n = 1000;
    let a = 0, b = 1;
    for (let i = 0; i < n; i++) {
      [a, b] = [b, a + b];
    }
})
.add('sem desestruturação', function () {
    const n = 1000;
    let a = 0, b = 1;
    for (let i = 0; i < n; i++) {
        let tmp = a;
        a = b;
        b += tmp;
    }
})
.add('desestruturação BigInt', function () {
    const n = 1000000n;
    let a = 0n, b = 1n;
    for (let i = 0n; i < n; i++) {
      [a, b] = [b, a + b];
    }
})
.add('sem desestruturação BigInt', function () {
    const n = 1000000n;
    let a = 0n, b = 1n;
    for (let i = 0n; i < n; i++) {
        let tmp = a;
        a = b;
        b += tmp;
    }
})
.on('complete', function () {
    console.log('Fastest is ' + this.filter('fastest').map('name'));
})
.on('cycle', function (event) {
    console.log(String(event.target));
})
.run({
    'async': true
});

Testei só no Linux, porque no Mac já vimos que BigInt fica bem mais lento usando o Bun.

Resultados (em operações por segundo, ou seja, quanto mais, melhor):

TesteNodeBun
desestruturação236.3873.920.325
sem desestruturação1.085.0753.897.458
desestruturação BigInt0,110,11
sem desestruturação BigInt0,120,11

Ou seja, parece que a implementação da desestruturação no Bun é mais rápida que no Node. Basta ver que sem desestruturação e sem BigInt, o Node pulou de 236 mil para 1 milhão de operações por segundo (e mesmo assim não chegou perto do Bun).

Mas quando BigInt é usado, a diferença já não foi tão grande. Imagino que neste caso o overhead dos cálculos parece ter um impacto maior que a desestruturação.

2

Intrigante o resultado, o Bun é feito com o motor WebPack, que roda no Safari, ao invés do V8. Teoricamente o Bun deveria ir melhor no hardware da Apple, já que utiliza um recurso que a própria Apple utiliza e provavelmente otimiza por padrão.

Outro adendo importante é que o Bun recomenda que esses tipos de teste em apps cli sejam feitos com o hyperfine

3

Vou compartilhar o resultado do benchmark com o M1 Pro:

~/projects/bun-benchmark  node index.js
test x 0.17 ops/sec ±2.08% (5 runs sampled)
 ~/projects/bun-benchmark  bun index.js
test x 0.07 ops/sec ±1.74% (5 runs sampled)

Executei o teste algumas vezes, e em todas elas, o node foi em média 2x mais rápido.

De fato, parece que o node é mais rápido no M1/M1 Pro (e ainda melhor no M1 Max), porém mais lento (ou praticamente igual) no Intel.

1
2

Eu vou postar um video amanhã sobre e vou colocar o link do TabNews nele

Opa, que bom que avisou sobre o vídeo dessa vez! hahaha

Pra aguentar o volume de acessos vindos pelo seu vídeo, já vou trocar aqui o Node pelo Bun no TabNews... 😜

Não, péra! Vendo os comparativos, é melhor adicionar uns pentes de memória e trocar o HD por SSD pra dar um gás no servidor 🤣🚀

Falando sério agora... É muito massa que sempre vem um pico de acessos e de novos cadastros quando você fala do TabNews nos seus vídeos. 💪🚀

6

Fiz alguns testes também sem bibliotecas em uma máquina linux com fedora 38 (dell Intel® Core™ i5-8350U × 8)

1. Teste de Operações com BigInt (Números de Fibonacci):

console.time("Execution Time");
const n = 1000000;
let a = BigInt(0), b = BigInt(1);
for (let i = BigInt(0); i < n; i++) {
  [a, b] = [b, a + b];
}
console.timeEnd("Execution Time");

Resultados:
Node.js: 13.770s
Bun: 32.01s

2. Teste de Operações de I/O:

console.time("Execution Time");
const n = 1000000;
for (let i = BigInt(0); i < n; i++) {
    console.log("Item: " + i);
}
console.timeEnd("Execution Time");

Resultados:

Node.js: 17.787s
Bun: 5.27s

3. Teste de Alocação de Memória:

console.time("Memory Allocation Execution Time");
const n = 1000;
for (let i = 0; i < n; i++) {
    let arr = new Array(100000).fill(0);
}
console.timeEnd("Memory Allocation Execution Time");

Resultados:

Node.js: 2.162s
Bun: 1.78s

4. Teste de Manipulação de Strings:

console.time("String Manipulation Execution Time");
let result = "";
const baseString = "abcdefghijklmnopqrstuvwxyz";
const n = 100000;
for (let i = 0; i < n; i++) {
    result += baseString;
}
console.timeEnd("String Manipulation Execution Time");

Resultados:

Node.js: 9.487ms
Bun: 4.12ms

5. Teste de Manipulação de Arrays:

console.time("Array Operations Execution Time");
const n = 100000;
let arr = [];
for (let i = 0; i < n; i++) {
    arr.push(i);
}
for (let i = 0; i < n; i++) {
    let item = arr[i];
}
for (let i = 0; i < n; i++) {
    arr.pop();
}
console.timeEnd("Array Operations Execution Time");

Resultados:

Node.js: Média de 8.347ms
Bun: Média de 7.91ms

6. Teste de Operações Matemáticas Básicas:

console.time("Basic Math Operations Execution Time");
let result = 0;
const n = 1000000;
for (let i = 1; i <= n; i++) {
    result += i;
    result -= i;
    result *= i;
    result /= i;
}
console.timeEnd("Basic Math Operations Execution Time");

Resultados:

Node.js: Média de 11.115ms
Bun: Média de 15.423ms

Parece que o bun se destaca em operações de I/O, agora o Node.js tem uma vantagem em operações aritméticas, especialmente com BigInt

5

Só de curiosidade, refiz esses testes usando o Benchmark.js. Só adicionei mais um caso, de Fibonacci sem BigInt, pois vi que deu diferença. Segue o código:

var Benchmark = require('benchmark');
var suite = new Benchmark.Suite;

suite
.add('Fibonacci BigInt', function () {
    const n = 1000000n;
    let a = BigInt(0), b = BigInt(1);
    for (let i = BigInt(0); i < n; i++) {
      [a, b] = [b, a + b];
    }
})
.add('Fibonacci sem BigInt', function () {
    // tive que diminuir n para não estourar o valor máximo de Number
    const n = 1000;
    let a = 0, b = 1;
    for (let i = 0; i < n; i++) {
      [a, b] = [b, a + b];
    }
})
.add('console.log no loop', function () {
    const n = 1000000;
    for (let i = BigInt(0); i < n; i++) {
        console.log("Item: " + i);
    }
})
.add('Alocação de memória', function () {
    const n = 1000;
    for (let i = 0; i < n; i++) {
        let arr = new Array(100000).fill(0);
    }
})
.add('Manipulação de strings', function () {
    let result = "";
    const baseString = "abcdefghijklmnopqrstuvwxyz";
    const n = 100000;
    for (let i = 0; i < n; i++) {
        result += baseString;
    }
})
.add('Manipulação de arrays', function () {
    const n = 100000;
    let arr = [];
    for (let i = 0; i < n; i++) {
        arr.push(i);
    }
    for (let i = 0; i < n; i++) {
        let item = arr[i];
    }
    for (let i = 0; i < n; i++) {
        arr.pop();
    }
})
.add('Operações matemáticas', function () {
    let result = 0;
    const n = 1000000;
    for (let i = 1; i <= n; i++) {
        result += i;
        result -= i;
        result *= i;
        result /= i;
    }
})
.on('cycle', function (event) {
    console.log(String(event.target));
})
.run({
    'async': true
});

Os resultados estão abaixo (os valores são de operações por segundo, ou seja, quanto mais, melhor).

Linux (Ubuntu 22.04.3, processador 11th Gen Intel® Core™ i5-1145G7 2.60GHz, 8 núcleos):

TesteNodeBun
Fibonacci BigInt0,070,07
Fibonacci sem BigInt2241543507428 (sim, mais de 3 milhões)
console.log no loop0,140,31
Alocação de memória2,512,54
Manipulação de strings22601024
Manipulação de arrays6491882
Operações matemáticas198222

Mac (2012, processador 2.6 GHz Quad-Core Intel Core i7):

TesteNodeBun
Fibonacci BigInt0,120,03
Fibonacci sem BigInt1364821547414
console.log no loop0,080,18
Alocação de memória1,492,7
Manipulação de strings14831033
Manipulação de arrays4531374
Operações matemáticas122118

Ou seja, o Node em geral se sai pior no console.log dentro de um loop (pelos motivos já citados em vários comentários). O cálculo com BigInt faz muita diferença no Mac (o Bun se sai pior), mas no Linux dá empate técnico. Em compensação, sem BigInt o Bun ganha por uma diferença enorme. E nas outras operações ora um, ora outro se sai melhor, com variados graus de vantagem.

4

wow! muito massa quebrar em 6 tipos de op. ficou muito bom ver a diferença!

IMPORTANTE: console.log nem sempre vai ser I/O, no NodeJS de fato ele vai virar um process.stdout.write() e olhando pro codigo fonte do bun:

but it's important that console.log() is fast.

No bun o console.log vai virar um synchronous printf vs. no NodeJS que se vai ser um asynchronous com stdout

3

Talvez a perda de performance seja por conta do arquivo não ser em typescript, temos esse trecho na página do Bun:

Bun is fast, starting up to 4x faster than Node.js. This difference is only magnified when running a TypeScript file, which requires transpilation before it can be run by Node.js.

Me parece um artifício para que as pessoas tenham algum hype de o Bun ser um Node Killer. Mas lendo o que eles mesmo escreveram da pra perceber que o foco maior não é na performance em si, mas em reduzir a complexidade de projetos que antes precisavam de 5 libs diferentes para rodar um simples arquivo.

Tenho curiosidade também de ver comparações em outros contextos, como operações de IO, com os módulos nativos do Bun e também com algoritmos recursivos. Vou tirar um tempo no próximo fim de semana para fazer algo do tipo.

2

O trecho citado fala de tempo de inicialização, então para comparar corretamente teria que fazer um benchmark que chama várias vezes o node e o bun, ao invés de repetir varias vezes o mesmo código.

4

Isso acaba reforçando o que eu disse sobre o hype. O que tem sido divulgado é que o Bun é mais rápido que o Node no geral, mas isso não está em lugar nenhum.

Ele é mais rápido em determinadas situações, como essa do typescript (comparando com ts-node e tsx), na leitura de arquivos, testes e etc. Se comparar nessas situações e nos casos mostrados provavelmente vai ser mais rápido mesmo, mas não quer dizer que será em todas as situações.

Eu acredito que o Bun vai conseguir crescer, difere te do Deno. Eu penso nisso pois o Bun abstrai e melhora muita coisa que para utilizarmos no Node precisamos de inúmeras libs. A experiência de desenvolvimento com o Bun é no geral melhor do que com o Node e é esse o forte do Bun. Mas tudo isso seria inútil se não houvesse compatibilidade com o próprio Node, um dos motivos que acabou com o destaque do Deno.

O fato de poder utilizar o Bun como uma alternativa a npm, yarn e pnpm é uma das coisas que irá fazer ele ter destaque, mas o que mais me impressionou foi a suíte de testes nativa.

O Bun matou a pau com essa suíte de testes e com o tempo só irá melhorar. Basicamente para migrar um projeto atual com Jest para Bun não é preciso fazer praticamente nada. No caso só seria necessário fazer as importações, mas se você importa as funcionalidades do Jest de @jest/globals ou utiliza Vitest, então então não precisa fazer nada e pode até mesmo desinstalar essas dependências.

O Bun também já vem com dotenv e bcrypt nativos, o que retira a necessidade de duas libs no seu projeto.

Se for para utilizar websockets, o Bun também tem um objeto nativo para isso, diferente do Node.

Só no que eu falei aqui já retiraria de 3 a 5 dependências da maioria dos projetos que utilizam o Node, isso que nem falei sobre não serem necessários bundlers no projeto, que já reduziria quase que pela metade o tamanho da node_modules.

Com tudo isso, o Bun não precisa necessariamente ser mais rápido que o Node, porque ele já é melhor que o Node. Eu digo isso não por achar que ele vai realmente matar o Node, mas porque ele tem duas vantagem principais: não precisa se preocupar com a compatibilidade com versões anteriores e não é o primeiro a fazer o que faz. Muitas melhorias no Node não são possíveis de implementar rapidamente por conta de compatibilidade e outras porque ele apenas foi o primeiro a implementar (inclusive algumas coisas antes da própria linguagem ter algum tipo de padrão). Isso faz com que o Bun possa implementar muita coisa rapidamente, como já vem fazendo, principalmente porque tem todo o histórico do node para saber o que é melhor ou pior de se fazer.

Mas agora, falando de estabilidade, o Node está anos luz a frente do Bun, afinal são 14 anos no mercado.

1

Concordo com tudo o que você disse, e adiciono de que nada impede que com o tempo o node também implemente e faça algumas melhorias no sentido de melhora da experiencia de desenvolvimento, diminuindo a "vantagem" do bun. Na versão 20.6 se não me engano, já existe o suporte nativo a arquivos .env, tirando a necessidade de pacote adicional, com o tempo acredito que o mesmo pode acontecer com outras libs e funcionalidades. Talvez o problema seja a retrocompatibilidade, mas isso toda e qualquer técnologia está sujeita, vai do desenvolvedor refatorar e atualizar.

1

Foi a mesma coisa quando o yarn chegou com inúmeras vantagens, e o npm incluiu todas elas posteriormente, de modo que hoje não se tem tanta vantagem em usar yarn. Daí surge o pnpm, com outras vantagens, e o npm corre atrás.

É a vantagem de ter uma concorrência. A tecnologia sempre vai evoluir para não ficar para trás.

2

Adicionando misterio a essa desafio:

Se fizer o loop a seguir sem a matematica do Fibonacci o Bun desempenha melhor que o NodeJS.

const n = 1000000
for (let i = BigInt(0); i < n; i++) {
    console.log("Item: " + i);
}
2

Depende de que forma mais rapido, o problema do node não é só em velocidade de execução, o tooling é uma merda, transpilar e configurar um projeto ts é chato. Com bun tu tem praticamente tudo isso out of the box. Sendo que o tooling é inclusive mais rapido que o pnpm. E a loucura de esmodule e common js, tudo isso é horrível no node.

2

Eu fiz alguns benchmarcks hoje nessa mesma pegada, mas ao invés de utilizar só Fibonacci eu fiz juntamente com outros algoritmos e também utilizando os respectivos recursivos nos casos onde se aplicam.

Na minha máquina que é Intel os resultados realmente foram muito discrepantes, o Bun deu de lavada no Node, o que me faz acreditar que nesses casos relatados tem algo a ver com os chips M1 mesmo.

Eu fiz um repositório com todas as implementações e o resultados dos testes. Se quiserem ver é esse aqui: https://github.com/Gabriel-Tapes/bun-vs-node.

Os testes foram até que bem simples, mas vou dar uma resumida aqui:

Eu utilizei o hyperfine para fazer os benchmarcks em uma configuração de 50 testes warmup antes de rodar o benchmarck real, que rodava os arquivos 1000 vezes.

Os meus resultados estão aqui: https://github.com/Gabriel-Tapes/bun-vs-node/blob/main/results.md

2

Executei uma série de benchmarks na minha máquina com as seguintes especificações:

  • WSL 2 Ubuntu 22.04.2 LTS
  • AMD Ryzen 7 5700x 4.6Ghz, 8 núcleos
  • RAM 16gb 3200Mhz CL18

Utilizei uma suíte de testes fornecida pelo @kht, que incluiu os seguintes casos de teste:

var Benchmark = require('benchmark');
var suite = new Benchmark.Suite;

suite
.add('desestruturação', function () {
    const n = 1000;
    let a = 0, b = 1;
    for (let i = 0; i < n; i++) {
      [a, b] = [b, a + b];
    }
})
.add('sem desestruturação', function () {
    const n = 1000;
    let a = 0, b = 1;
    for (let i = 0; i < n; i++) {
        let tmp = a;
        a = b;
        b += tmp;
    }
})
.add('desestruturação BigInt', function () {
    const n = 1000000n;
    let a = 0n, b = 1n;
    for (let i = 0n; i < n; i++) {
      [a, b] = [b, a + b];
    }
})
.add('sem desestruturação BigInt', function () {
    const n = 1000000n;
    let a = 0n, b = 1n;
    for (let i = 0n; i < n; i++) {
        let tmp = a;
        a = b;
        b += tmp;
    }
})
.on('complete', function () {
    console.log('Fastest is ' + this.filter('fastest').map('name'));
})
.on('cycle', function (event) {
    console.log(String(event.target));
})
.run({
    'async': true
});

Os resultados obtidos foram os seguintes:

TesteNodeBun
desestruturação1.350.540884.238
sem desestruturação1.392.0951.662.632
desestruturação BigInt0.160.10
sem desestruturação BigInt0.160.10
1

Só queria agregar aqui tenho um notebook que está usando o POP_OS 22.04 e rodei o bun com o zsh. esse mesmo Fibonacci demorou em média 14s enquanto o node demorou 12s.

tenho um i7 de 8ª geração. Durante a execução estava com o Google Chrome, Egde, VS Code aberto.

1

Alo pessoal. A galera ja comentou sobre diversos fatores que podem influenciar aqui... so queria compartilhar os resultados de uma maquina mais "humilde" (que nao é humilde pq no fim eh um mac mas vcs entenderam...)

Configuracao

MacBook Air 2020 1,6 GHz Dual-Core Intel Core i5, no macOS Ventura 13.4 (22F66)
Node --v 18.17.1
Bun -version 1.0.2

Teste

console.time("execution time")

function fib(n) {
  let a = BigInt(0), b = BigInt(1);
  for (let i = BigInt(0); i < n; i++) {
    [a, b] = [b, a + b]
    console.log("a:", a, "b:",b)
  }
  return a.toString()
}

for(let i = 100; i > 0; i--){
  let result = fib(10000)
  console.log(result)
}

console.timeEnd("execution time")

Resultado

RuntimeTempo total(m:ss.mmm)Média(ss.mmm)
Node7:06.11404.261
Bun9:42.0605.821
1

Ao que parece ser realmente é a forma como a compilação do bun em arm64 está funcionando, tem alguns issues acerca da arquitetura, erros que não ocorrem na compilação realizada para amd/intel. Pode ser que seja alguma implementação para arm64 que faz com que seja mais rápido.

Um dos principais core do projeto (Jarred-Sumner), fez a seguinte fala:
If you run into performance issues with real code in your application feel free to file an issue

This looks more like a microbenchmark. JS engines are really good at optimizing microbenchmarks with statically known data. It probably isn't meaningful in applications.

logo:

Se você tiver problemas de desempenho com código real em seu aplicativo, sinta-se à vontade para registrar um problema

Isso se parece mais com um microbenchmark. Os mecanismos JS são realmente bons para otimizar microbenchmarks com dados estaticamente conhecidos. Provavelmente não é significativo em aplicativos.

https://github.com/oven-sh/bun/issues/3358

Sobre o código:

`var appendDelay = 1;
var chartLen = 10;
var delaysSoFar = 0;
var arr = [0];

function update(x) {
if (arr.length < chartLen) {
if (delaysSoFar == appendDelay) {
arr.push(x);
delaysSoFar = 0;
} else {
arr[arr.length - 1] = x;
}
delaysSoFar += 1;
}

if (arr.length == chartLen) {
for (var i = 0; i < 10; i += 2) {
arr[i / 2] = arr[i];
}
arr.length = 5;

appendDelay *= 2;
arr.push(x);
}
}

const now = performance.now();
for (var i = 0; i < 1000000000; i++) {
update(i);
}
console.log(performance.now() - now);

console.log(arr);`

obs.
~~
Outro detalhe que eu acredito fazer parte do processo é que mesmo o webpack sendo apple, ela roda em outras máquinas que utilizam o webkit como o gnome epiphany (browser do gnome uma DE linux). Então não acredito que seja isso que possa fazer alguma diferença. ~~
E um ultimo detalhe é Lucas sempre vejo seus videos, bom trabalho.

0

Que balde de água fria, hein.
Está o maior hype que o bun é mais rápido porque foi feito em Rust.
Um monte de gente repetindo que bun é mais rápido, porém a realidade é que praticamente estão empatados.
Supostamente pelo menos bun é mais seguro, mas não duvido alguém fazer testes reais e descobrir falhas.
Nota: Estou repetindo feito papagaio que o Rust é seguro, por ouvir que ele é seguro, mas não manjo de segurança e claramente nunca testei a segurança de Rust.

2

Mas o Bun é escrito em Zig, não tem nada de Rust no código dele, veja. Quem foi feito em Rust é o Deno.

De qualquer forma, pelos testes básicos que fiz, e por vários benchmarks que vi por aí, em alguns casos o Bun foi mais rápido sim.

Mas sempre tem quem compra o marketing cegamente e entende "então é sempre mais rápido". Não é. Até onde vi, depende do caso, tem vezes que é melhor, em outras é pior e em outras tanto faz.

Como eu disse no outro comentário, o ideal é que cada um testasse para ver se no seu caso específico faz diferença, e então decidir se vale a pena. Infelizmente, parece que muitas pessoas preferem acreditar na solução mágica que sempre vai ser melhor pra tudo. E pra essas, qualquer coisa será um balde de água fria, pois tal solução não existe.

-2
2
1

Está pasando verganha dizendo algo que você claramente não sabe, está apenas repetendo igual um papagaio o que ouviu por ai, se eu fosse você já teria apagado esse post correndo!