DEV Community

Cover image for Node.js Por Baixo Dos Panos #5 - Hidden Classes e Alocações de Variáveis
Lucas Santos
Lucas Santos

Posted on • Edited on

Node.js Por Baixo Dos Panos #5 - Hidden Classes e Alocações de Variáveis

(Foto de capa por Jose Gabriel Ortega Castro no Unsplash)

Na última parte desta série, discutimos um pouco sobre Abstract Syntax Trees e como a V8 compila nosso código. Outra coisa interessante que o V8 faz ao lidar com o JavaScript é que possibilita que uma linguagem estaticamente tipada, como C ++, execute código de uma linguagem dinamicamente tipada, como JS. Um dos exemplos mais simples que temos de tipagem dinâmica é uma declaração de objeto:

const myObj = {}
console.log(myObj) // {}

myObj.x = 1
console.log(myObj) // { x: 1 }

myObj.y = 2 // Dynamically changing the type
console.log(myObj) // { x: 1, y: 2 }
Enter fullscreen mode Exit fullscreen mode

Como o JavaScript é uma linguagem dinâmica, as propriedades de nossos objetos podem ser adicionadas e removidas em tempo real - como fizemos. Essas operações requerem uma pesquisa dinâmica para resolver onde o local dessa propriedade está na memória para que a linguagem possa recuperá-lo para você. As pesquisas dinâmicas são uma operação de alto custo para processadores. Então, como o V8 lida com isso para tornar o JS tão rápido? A resposta é classes ocultas (em inglês Hidden Classes). E é um dos truques de otimização pelo qual o V8 é tão famoso.

Falaremos sobre outras técnicas de otimização do compilador posteriormente nos próximos artigos

Geralmente, quando temos linguagens estaticamente tipadas, podemos determinar facilmente onde uma propriedade está na memória, pois todos os objetos e variáveis são determinados por um layout de objeto fixo que você definirá como seu tipo, e novas propriedades não podem ser adicionadas durante o tempo de execução, isso facilita bastante para o compilador encontrar os valores (ou ponteiros) dessas propriedades na memória, pois eles podem ser armazenados como um buffer contínuo com um offset fixo entre cada objeto. E esse offset pode ser facilmente determinado pelo tipo de objeto, pois todos os tipos têm um valor fixo de memória. O V8 aproveita esse conceito de layout fixo para usar a abordagem de uma classe oculta. Vamos ver como isso funciona:

Para cada tipo de objeto, o V8 cria uma classe oculta, portanto nossa primeira declaração de const myObj = {} criaria uma classe como esta:

Agora, à medida que adicionamos uma nova chave ao myObj, o V8 cria uma nova classe oculta baseada em C0 (copiando-a) chamada C1 e atualiza o C0 para adicionar uma transição para C1:

Agora, como a última instrução que adicionamos y, ela executa exatamente os mesmos passos de antes. Cria uma nova classe C2 baseada em C1, adicione uma nova transição para C1 apontando para C2:

Este pequeno truque possibilita ao V8 reutilizar classes ocultas para um novo objeto. Se criarmos um novo objeto como {}, nenhuma nova classe será criada; em vez disso, o V8 apontará o novo objeto para C0. À medida que adicionamos as novas propriedades x e y, o novo objeto apontará para as classes C1 e C2 que gravam os valores nos offsets especificados por essas classes. Esse conceito possibilita que um compilador ignore uma pesquisa do tipo "dicionário" para quando uma propriedade é acessada. Como ele já sabe para qual classe o objeto aponta e onde está o offset para essa propriedade, ele pode simplesmente ir direto para lá. Isso também torna a V8 capaz de usar otimizações baseadas em classe e inline caching - que veremos mais adiante.

No entanto, as classes ocultas são extremamente voláteis, são únicas para um tipo específico de objeto. Portanto, se trocarmos a ordem de nossas propriedades por y e x em vez do oposto, o V8 teria que criar novas classes ocultas, pois C1 só possui offsets para x na posição 0 e C2 somente offsets para y na primeira posição.

Mas lembre-se isso é feito no C++ porque JavaScript é uma linguagem baseada em protótipos e, portanto, não possui classes.

Conclusão

Esta foi apenas uma breve explicação sobre como o V8 lida com a estrutura interna do JavaScript. Compreender a alocação interna de variáveis e a criação interna de objetos nos permite entender como podemos escrever código melhor e mais performático.

Não deixe de acompanhar mais do meu conteúdo no meu blog e se inscreva na newsletter para receber notícias semanais!

Top comments (1)

Collapse
 
deyvisonrocha profile image
Deyvison Rocha

Ótimo artigo man!