Por que o ES6 nativo promete mais lento e consome mais memória que o bluebird?

195

Nesse benchmark , o conjunto leva quatro vezes mais tempo para concluir as promessas do ES6 em comparação com as promessas do Bluebird e usa 3,6 vezes mais memória.

Como uma biblioteca JavaScript pode ser muito mais rápida e mais leve que a implementação nativa da v8, escrita em C? As promessas do Bluebird têm exatamente a mesma API que as promessas nativas do ES6 (além de vários métodos de utilidade extras).

A implementação nativa está mal escrita ou há algum outro aspecto disso que estou perdendo?

callum
fonte
Lembre-se de que as implementações modernas de JavaScript são altamente otimizadas e podem até executar nativamente usando o JIT .
11
De acordo com este benchmark , o BlueBirdJS é realmente mais lento que o Native Promises. Mas, PromiseMeSpeedJS realmente supera os dois. Uma das muitas coisas que o PromiseMeSpeedJS prova disso é que o principal culpado por desempenho é o uso abusivo e abusivo do newoperador, porque o PromiseMeSpeedJS não usa new.
Jack Giffin
11
@JackGiffin Chrome 67: PromiseMeSpeedJS é 46% mais lento e Bluebird é 61% mais lento.
precisa saber é o seguinte

Respostas:

272

Autor Bluebird aqui.

A implementação do V8 promete ser escrita em JavaScript, não em C. Todo o JavaScript (incluindo o próprio V8) é compilado no código nativo. Além disso, o JavaScript escrito pelo usuário é otimizado, se possível (e vale a pena), antes de ser compilado no código nativo. A implementação de promessas é algo que não beneficiaria muito ou nada de ser escrito em C, na verdade, só o tornaria mais lento porque tudo que você está fazendo é manipular objetos e comunicação JavaScript.

A implementação da V8 simplesmente não é tão otimizada quanto o bluebird, por exemplo, aloca matrizes para manipuladores de promessas . Isso requer muita memória quando cada promessa também precisa alocar algumas matrizes (a referência cria promessas gerais de 80k, de modo que são 160k matrizes não utilizadas alocadas). Na realidade, 99,99% dos casos de uso nunca ramificam uma promessa mais de uma vez, e a otimização desse caso comum obtém enormes melhorias no uso da memória.

Mesmo que o V8 implementasse as mesmas otimizações que o bluebird, ele ainda seria prejudicado pela especificação. O benchmark deve ser usado new Promise(um antipadrão no bluebird), pois não há outra maneira de criar uma promessa raiz no ES6. new Promiseé uma maneira extremamente lenta de criar uma promessa; primeiro, a função executora aloca um fechamento; depois, são passados ​​2 fechamentos separados como argumentos. São 3 fechamentos alocados por promessa, mas um fechamento já é um objeto mais caro do que uma promessa otimizada.

O Bluebird pode usar, o promisifyque permite muitas otimizações e é uma maneira muito mais conveniente de consumir APIs de retorno de chamada e permite a conversão de módulos inteiros em módulos baseados em promessa em uma linha ( promisifyAll(require('redis'));).

Esailija
fonte
10
"ainda seja dificultado pela especificação" - Não tenho certeza do que isso significa. Você está dizendo que o ES6 está seguindo uma especificação que é inerentemente lenta? Se esse for o caso, isso significa que o bluebird não está seguindo a mesma especificação (e, nesse caso, está seguindo uma diferente e qual)? E existe alguma razão para o ES6 não ter uma maneira melhor de criar uma Promessa raiz além new Promiseou melhorar a instanciação para torná-la mais barata (como não criar 3 fechamentos por instância)?
Anthony
12
Isso não parece nada bom (para JS). Eu realmente não quero usar uma biblioteca Promise quando houver uma implementação interna. Esta é uma situação mais do que infeliz para todos, se tudo isso for verdade. Mas já tenho problemas para ver o Promise-hype, escrevi 100.000 aplicativos LoC JS e ainda não vejo nenhuma necessidade real disso, é uma melhoria muito pequena se for para mim , principalmente no tratamento de erros, não melhoria no tratamento de retorno de chamada (nunca estive no "inferno de retorno de chamada" com meu estilo de codificação).
Morre
19
No ES6, você não pode usar Promise.resolve()para criar uma "promessa raiz"?
zetlen
10
@ MörreNoseshine (continuação) Anos mais tarde, os autores do ES6 apareceram e disseram "ei, vamos especificar que os mecanismos JS devem fornecer um utilitário genérico em conformidade com o Promises / A + pronto para uso, para que as pessoas sempre tenham uma ferramenta básica de promessa à mão " Essa é uma boa conveniência (não é necessário importar uma biblioteca apenas para fazer uma rápida Promise.resolve()ou qualquer outra coisa), mas é uma implementação muito básica e sua existência não deve desencorajar o uso de ferramentas relacionadas a promessas mais sérias, como o bluebird!
Callum
11
@ MörreNoseshine Aplicativo Javascript LOC de 100k que provavelmente nunca teve nenhuma funcionalidade assíncrona. Boa sorte escrevendo um jogo de 100k LoC JS com uma biblioteca mysql / redis sem bluebird.
NiCk Newman