JSON.parse/stringify: performance em JS | guia prático
JS não é linguagem 'rápida', mas JSON.parse e stringify historicamente brilharam. O que isso muda em APIs, cache e decisões de serialização
Resposta direta
Antes de otimizar microbenchmarks exóticos, entenda por que o caminho JSON nativo ainda é o default sensato. Até eu usá-lo e então faz sentido porque ele atira tanto c****** é tão bom eles recentemente apresentaram agentes de fundo para que possam abrir prs para você no cloud com todas as outras coisas que eu tentei para isso eu não confio muito em eles porque se eu não consigo dirigir isso na minha.
JS lento vs JSON surpreendentemente rápido
Da implementação do JSON Stringify, para a performance do quirk e do V8 para esse tipo de coisa, para uma das minhas fatos divertidas, que é que muitos objetos são mais fáceis de definir, como se fossem carregados mais rápido no VM. Eu acho que as pessoas não apreciam o quanto vai para o JSON para fazer essas simples coisas que nós nos dependemos todos os dias, tão rápido quanto elas são na maioria dos casos de uso. Há muitas grandes empresas começando a fazer o movimento, e eu até vi isso comigo mesmo, porque nós só runhamos um par de ades com Augment, e eles são regularmente o número um na nossa lista para o sponsor que vocês gostam mais. Até eu usá-lo e então faz sentido porque ele atira tanto c****** é tão bom eles recentemente apresentaram agentes de fundo para que possam abrir prs para você no cloud com todas as outras coisas que eu tentei para isso eu não confio muito em eles porque se eu não consigo dirigir isso na minha máquina, eu não confio que vai chegar a qualquer lugar, mas como eles têm o contexto da base de código como qualquer outra uma dessas ferramentas, eles são muito mais possíveis de conseguir as coisas certas e você pode ver isso com como eles derrubaram a banca sw e é o melhor resultado até o momento em que eles construíram algo muito, muito bom em resolver problemas difíceis com o código.
É por isso que estamos animados a compartilhar que um esforço de engenharia recente fez Json para Stringify e V8 mais do que duas vezes mais rápido. Json.parse json.stringify foo ow é um padrão comum para DeepClone ow isso me assusta então lembre-se que queremos um passo rápido sem efeito de lado, queremos algo que seja realmente rápido e não causar efeitos de lado estranhos a base para essa otimização é um novo passo rápido que foi construído em uma premissa simples se podemos garantir que serializar um objeto não triga nenhum efeito de lado e podemos usar uma implementação especializada muito mais rapidamente um efeito de lado neste contexto é qualquer coisa que quebra a simples traversação de um objeto. Isso inclui não apenas os casos mais óbvios, como executar um código usado para encontrar durante a serialização, mas também operações internas mais subtil que podem provocar um ciclo de coleção de bagunça. Eu tenho muitos pensamentos sobre uma coleção de bagunça v8, como você pode ter visto no meu vídeo anterior, onde eu acabei de acidentemente adivinhar corretamente o que o autor estava errando e tinha que Isso foi divertido.
Isso permite que ele ultrapasse muitos cheques caros em lógica defensiva que foram necessários pelo serializador de propósito geral, resultando em uma velocidade significativa para os tipos mais comuns de objetos JavaScript que representam dados plenos. A escolha arquitetônica não só elimina a necessidade de cheques de overflow de stack e nos permite resumir rapidamente após mudanças de encodamento, mas também nos permite serializar os desenvolvedores em grafos de objetos mais profundamente nestados do que antes. Porém, se uma string contém apenas um único personagem que está fora da área assígrafa, todos os personagens na string vão usar uma representação de 2 bytes em vez disso, o que, essencialmente, vai duplicar a utilização de memória. Então, durante a serialização, devemos já inspectar cada tipo de instância de string para detectar se a representação não pode ser tratada no passo rápido, porque novamente, constring pode provocar coletivo de barracos durante o flattening, porque se não mais precisa dos valores anteriores que você adicionou quando fez a nova string, ou a nova string agora te coloca sobre os limites da sua memória, a coletiva de barracos tem que entrar para limpar qualquer memória que não estava sendo usada antes.
Parse e stringify no caminho crítico
Vamos imaginar que em vez de apenas logar o const, definimos isso como MESSAGE e então console.log message agora me entretenha aqui eu sei que mais computadores além de como antigos iphones tem o suficiente memória para encaixar todo esse conteúdo de texto totalmente bem mas digamos que hipoteticamente você saiu da memória quando você adicionou esses juntos quando você criou mensagem apendendo esses você está agora sobre o limite de memória isso iria provocar uma coleção de bolo onde v8 o motor vai procurar todas as coisas que não estão mais sendo referenciadas e de-referenciá-las. O ponto aqui é que a criação desta nova string, em particular, em consumo, porque eu apenas aprendi que estas são na verdade, evaluações de forma lésbica, então quando você a define, ela não é consumida. Então, a este ponto, se eu console.log some message, agora a computação será completada, a linha será escapada juntos E o nome que passamos aqui não é mais relevante, porque esse código já foi executado. Jsonified equals json.stringify some message this is the point at which the addition actually happens there are other things in this file that no longer are relevant at this point in time we don't care anymore about this string because it's already been appended and added and calculated here it's lazily done but more importantly when it's lazily done it now has the opportunity to clean up things that don't matter anymore so when we call this json stringify Esta string, que pode ainda estar na memória, não precisa mais existir.
E o cheque que eles fazem para descobrir se isso vai provocar uma coleção de barras ou não, também controla se eles vão precisar usar o byte 1 ou o encoding byte 2. A estratégia garantiu que nos mantivemos em um caminho altamente optimizado para o caso comum, enquanto a transição para lidar com personagens de 2 bytes é leve e eficiente. Isso nos permite carregar uma parte muito maior da corda em um registro de SIMD amplo e verificar múltiplos bytes para qualquer personagem que escapa, tudo de uma vez, com apenas algumas instruções. Isso é tudo para estrelas mais longas, mas para estrelas mais curtas, onde o custo de instruções de hardware é muito alto, usamos uma técnica chamada SWAR, que é SIMD com um registro.
É meio engraçado que JavaScript é a língua mais avançada do mundo, e que de muitas maneiras, JSON em V8 é mais avançado e tecnicamente. Um dos trabalhos mais difíceis de engenharia, um dos mais avançados, de optimização de baixo nível, que foram feitos na história da software, foram feitos para fazer o JSON ser mais rápido no V8 para Chrome. Então temos 2x mais paquetes de NPM do que temos de linhas de código em V8, mas o fato de que esses sejam um pouco próximos é realmente engraçado. Aparentemente, se você incluir comentários, adicionais de código, testes e tudo mais, as linhas totalizadas são quase 3 milhões.
Leitura útil
Antes de otimizar microbenchmarks exóticos, entenda por que o caminho JSON nativo ainda é o default sensato. Use o trecho acima como restrição, não como citação ornamental.
Quando sair do JSON nativo
De forma geral, o passo rápido deve iterar sobre as propriedades de um objeto e, para cada chave, faça uma série de comprovações, confirme que a chave não é um símbolo, certifique-se que é inumerável e, finalmente, scanne a linha para personagens que requerem escapar. Há muitos detalhes de implementação que não temos necessariamente acesso a no layer de JavaScript que estamos em, mas eles estão lá para que o V8 possa fazer optimizações. Então o que isso faz é que se você tem uma forma de objeto que você usa múltiplas vezes, v8 vai notar e criar um struct para isso que pode usar para saber e identificar essa forma de objeto para mapeá-la e usá-la mais eficazmente. Então aqui temos uma função, mas nesta função usamos isso, o que você nunca deveria fazer, mas você pode fazer, e se o extra data não é um número, então colocamos em experiência, se não colocamos em prominência.
Então temos essas duas coisas que chamamos de chamar de novas peak, que ambos têm nome e altura, mas então este um tem prominência, porque colocamos um número, e este um tem experiência, porque colocamos uma linha. Então a chave aqui é que as locais de campo do objeto são mapeados em um nível inferior para que ele possa descobrir quando você pedir uma certa chave onde ela está em memória mais rápido. Aparentemente você tem que criar 7 objetos para completar a mapeagem e uma vez que você tenha feito isso, seus objetos de pique terão exatamente três propriedades de objeto sem possibilidade de adicionar mais diretamente no objeto. De novo, isso faz com que quando você tem uma forma comum para um objeto, ela possa mapear a chave para a posição que está armazenada na memória muito mais rapidamente.
Quando nós serializamos um objeto que tem a mesma classe escondida como um objeto que nós serializamos antes, que é bem comum, como em um array de objetos onde todos têm a mesma forma. Se você está tentando serializar um array de usuários que tem um monte de usuários do mesmo tipo, nós encontramos a classe escondida e sabemos como ela se serializa após a primeira, para que possamos usar essa consciência de serialização para não ter que recomputar para tudo mais neste array. Então, se também sabemos que na mapa 3 ou 4 não temos que fazer nada especial porque não temos que nos preocupar com coleções de bagunça ou efeitos de lado, então não temos que nos preocupar em nada, podemos literalmente apenas tirar dessas locações de memória direto para nossa nova string. Isso é enorme, e eu vou usar isso como uma tangência para uma das minhas principais notícias divertidas em JavaScript e V8, que é o fato, isso não é disputado, isso é real, que em muitos casos, é mais rápido definir um objeto passando-o como uma linha para JSON-parse do que seria apenas definir o objeto em linha.
Armadilhas de serializar objetos grandes
Porque a gramática de JSON é muito mais simples do que a gramática de JavaScript, o JSON pode ser parsado mais eficazmente do que o JS pode. Se você tem um objeto complexo que é o ponto de partida para sua aplicação, com muitas diferentes chaves, talvez alguns arrays loucos e todas essas outras coisas, JavaScript tem tantas coisas para parsar que. E agora, é ainda mais rápido, porque eles encontraram maneiras de remapar e reutilizar locais de memória e registros, baseados nas classes escondidas das coisas que você está serializando e deserializando. Seria difícil que essas sejam as coisas que estão fazendo sua app mais lenta, mas se você é o tipo que quer optimizar cada um dos corações, você provavelmente já aprendeu isso apenas por passar pelo profiler em Chrome.
It's kind of crazy to think that the way you turn a number into a string is so complex that there have been lots of different implementations and we have finally moved to a new one after however many fucking decades V8's been around for. E essa é uma implementação disso em C++, que agora foi transformada em V8, para que seus números se convertam em string mais rápido quando você json stringify. Enquanto a otimização foi dirigida pelo profilamento de stringify no JSON, o novo implementação do DragonBox beneficia todos os chamas para o número, o protótipo, o ponto de string, durante o V8. Em vez de colocar tudo em um único bloco de memória crescente, Agora usamos uma lista de buffers ou segmentos maiores alocados na memória da zona V8.
O estranho aqui é que se houver um erro de linha em qualquer um desses códigos, em teoria, com o Sandbox Escapes, pode causar RCEs para parcerizar o JSON. Para a maior parte de casos de uso, como dados serializados para respostas api ou objetos de configuração de cache, essas condições são naturalmente metidas, o que permite aos desenvolvedores se beneficiar das melhorias de performance automaticamente. O caminho de string de 2 tier, um para as strings de 1 byte e outro para as strings de 2 bytes Ele conseguiu a maioria disso Acontece que poderíamos ter criado uma vibe com o performance win Na serialização de string no V8 Quem teria pensado? De sua lógica de nível alto até sua memória core e operações de gestão de caráter, nós entregamos uma melhoria de 2x mais que a melhoria de performance medido no JET Stream 2, Json Stringify e Spectre Bench.
Checklist de medição antes de trocar formato
Eu acho que todos sabemos e entendemos isso. Mas você pode estar surpreso com o quão rápido o JSON é, especificamente JSON Parse e Stringify. Históricamente, ele foi uma das melhores implementações de transformar o JSON em objetos reais. O ponto é que é um objeto de JavaScript serializado.
Ele acabou de ficar duas vezes mais rápido. Bem, eu estaria mentindo, porque é quase três vezes mais rápido em muitos cenários. Eu estou absolutamente enganado com o trabalho que o time da Chrome acabou de colocar para fazer o JSON Stringify ainda mais rápido. Isso pode parecer pequeno e simples, mas tem realmente muita coisa legal para entrar aqui.
Da implementação do JSON Stringify, para a performance do quirk e do V8 para esse tipo de coisa, para uma das minhas fatos divertidas, que é que muitos objetos são mais fáceis de definir, como se fossem carregados mais rápido no VM. Se você os definir como JSON e então json.parse, então se você realmente definir o objeto diretamente. Eu acho que as pessoas não apreciam o quanto vai para o JSON para fazer essas simples coisas que nós nos dependemos todos os dias, tão rápido quanto elas são na maioria dos casos de uso. Muito como esse mudança, eu quero manter meu vídeo sem efeito, mas mais importante, eu quero manter ele livre em geral.
Próximo passo
Escolha uma métrica (tempo, retries, conversão ou qualidade aceita) e rode 7 dias antes de escalar o padrão.