Isso surgiu durante uma discussão com meu amigo sobre recursão. Por que não construir
um método Javascript JSON.stringify como um exercício de programação recursiva? Parece ótimo
ideia.
Eu rapidamente elaborei a primeira versão. E teve um desempenho horrível! O
o tempo necessário foi cerca de 4 vezes maior que o padrão JSON.stringify.
function json_stringify(obj) { if (typeof obj == "number" || typeof obj == "boolean") { return String(obj); } if (typeof obj == "string") { return `"${obj}"`; } if (Array.isArray(obj)) { return "[" obj.map(json_stringify).join(",") "]"; } if (typeof obj === "object") { const properties_str = Object.entries(obj) .map(([key, val]) => { return `"${key}":${json_stringify(val)}`; }) .join(","); return "{" properties_str "}"; } }
Executando o seguinte, podemos ver que nosso json_stringify funciona como
esperado.
const { assert } = require("console"); const test_obj = { name: "John Doe", age: 23, hobbies: ["football", "comet study"] }; assert(json_stringify(test_obj) === JSON.stringify(test_obj))
Para testar mais cenários e várias execuções para ter uma ideia média de como nosso
script é executado, criamos um script de teste simples!
function validity_test(fn1, fn2, test_values) { for (const test_value of test_values) { assert(fn1(test_value) == fn2(test_value)); } } function time(fn, num_runs = 1, ...args) { const start_time = Date.now() for (let i = 0; iExecutando isso, obtemos os tempos como os seguintes.
Testing 1000 times Std lib JSON.stringify() took 5 ms Custom json_stringify() took 20 ms Testing 10000 times Std lib JSON.stringify() took 40 ms Custom json_stringify() took 129 ms Testing 100000 times Std lib JSON.stringify() took 388 ms Custom json_stringify() took 1241 ms Testing 1000000 times Std lib JSON.stringify() took 3823 ms Custom json_stringify() took 12275 msPode funcionar de maneira diferente em sistemas diferentes, mas a proporção do tempo gasto
por std JSON.strngify ao nosso json_stringify personalizado deve ser sobre
1:3 - 1:4Poderia ser diferente também em um caso interessante. Continue lendo para saber mais sobre
que!Melhorando o desempenho
A primeira coisa que pode ser corrigida é o uso da função map. Ele cria
nova matriz da antiga. No nosso caso de objetos, estamos criando um array de
Propriedades de objeto stringificadas JSON fora da matriz que contém entradas de objeto.Coisa semelhante também está acontecendo com a stringificação dos elementos da matriz.
Temos que percorrer os elementos de um array ou as entradas de um objeto! Mas
podemos pular a criação de outro array apenas para juntar as partes stringificadas JSON.Aqui está a versão atualizada (apenas as partes alteradas mostradas por questões de brevidade)
function json_stringify(val) { if (typeof val === "number" || typeof val === "boolean") { return String(val); } if (typeof val === "string") { return `"${val}"`; } if (Array.isArray(val)) { let elements_str = "[" let sep = "" for (const element of val) { elements_str = sep json_stringify(element) sep = "," } elements_str = "]" return elements_str } if (typeof val === "object") { let properties_str = "{" let sep = "" for (const key in val) { properties_str = sep `"${key}":${json_stringify(val[key])}` sep = "," } properties_str = "}" return properties_str; } }E aqui está a saída do script de teste agora
Testing 1000 times Std lib JSON.stringify() took 5 ms Custom json_stringify() took 6 ms Testing 10000 times Std lib JSON.stringify() took 40 ms Custom json_stringify() took 43 ms Testing 100000 times Std lib JSON.stringify() took 393 ms Custom json_stringify() took 405 ms Testing 1000000 times Std lib JSON.stringify() took 3888 ms Custom json_stringify() took 3966 msIsso parece muito melhor agora. Nosso json_stringify personalizado está demorando apenas 3 ms
mais do que JSON.stringify para restringir um objeto aninhado profundo 10.000 vezes.
Embora isso não seja perfeito, é um atraso aceitável.Espremendo mais??
O atraso atual pode ser devido a toda a criação e concatenação de strings
isso está acontecendo. Cada vez que executamos elements_str = sep json_stringify(element)
estamos concatenando 3 strings.Concatenar strings é caro porque requer
- criando um novo buffer de string para caber em toda a string combinada
- copie strings individuais para o buffer recém-criado
Usando nós mesmos um Buffer e gravando os dados diretamente nele, pode nos dar
uma melhoria de desempenho. Como podemos criar um buffer grande (digamos 80 caracteres)
e, em seguida, crie novos buffers para acomodar mais 80 caracteres quando acabar.Não evitaremos totalmente a realocação/cópia de dados, mas iremos
reduzindo essas operações.Outro possível atraso é o próprio processo recursivo! Especificamente o
chamada de função que leva tempo. Considere nossa chamada de função json_stringify(val)
que possui apenas um parâmetro.Noções básicas sobre chamadas de função
As etapas seriam
- Empurre o endereço de retorno para a pilha
- envia a referência do argumento para a pilha
- Na função chamada
- Retirar a referência do parâmetro da pilha
- Retire o endereço de retorno da pilha
- empurre o valor de retorno (a parte stringificada) para a pilha
- Na função de chamada
- Retire o valor retornado pela função da pilha
Todas essas operações acontecem para garantir que as chamadas de função aconteçam e isso adiciona CPU
custos.Se criarmos um algoritmo não recursivo de json_stringify todas essas operações
listado acima para chamada de função (vezes o número de tais chamadas) seria
reduzido a nada.Esta pode ser uma tentativa futura.
Diferenças de versão do NodeJs
Uma última coisa a ser observada aqui. Considere a seguinte saída do script de teste
Testing 1000 times Std lib JSON.stringify() took 8 ms Custom json_stringify() took 8 ms Testing 10000 times Std lib JSON.stringify() took 64 ms Custom json_stringify() took 51 ms Testing 100000 times Std lib JSON.stringify() took 636 ms Custom json_stringify() took 467 ms Testing 1000000 times Std lib JSON.stringify() took 6282 ms Custom json_stringify() took 4526 msNosso json_stringify personalizado teve um desempenho melhor do que o padrão NodeJs
JSON.stringify???Bem, sim! Mas esta é uma versão mais antiga do NodeJs (v18.20.3). Acontece que, para
nesta versão (e talvez inferior também) nosso json_stringify personalizado funciona
mais rápido que a biblioteca padrão!Todos os testes deste artigo (exceto este último) foram feitos com
Nó v22.6.0O desempenho do JSON.stringify aumentou da v18 para a v22. Isso é tão bom
Também é importante observar que nosso script teve melhor desempenho no NodeJs v22.
Então, isso significa que o NodeJs também aumentou o desempenho geral do tempo de execução.
Possivelmente ocorreu uma atualização no próprio motor V8.Bem, esta foi uma experiência agradável para mim. E espero que seja para
você também. E no meio de toda essa diversão, aprendemos uma ou duas coisas!Continue construindo, continue testando!
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3