Transcript

00:00:00(música animada) - Ok, obrigado a todos, olá.
00:00:07Meu nome é Luke Sandberg.
00:00:09Sou engenheiro de software na Vercel,
00:00:10trabalhando no Turbo Pack.
00:00:12Estou na Vercel há cerca de seis meses,
00:00:15o que me deu tempo suficiente para vir aqui e falar sobre todo o ótimo trabalho que eu não fiz.
00:00:23Antes de vir para a Vercel,
00:00:24eu estava no Google,
00:00:26onde trabalhei nas nossas cadeias de ferramentas web internas e fiz coisas estranhas como construir um compilador de TSX para bytecode Java e trabalhar no compilador Closure.
00:00:37Então,
00:00:38quando cheguei à Vercel,
00:00:40foi como pisar em outro planeta,
00:00:43tudo era diferente.
00:00:45E fiquei bastante surpreso com tudo o que fizemos na equipe e com os objetivos que tínhamos.
00:00:50Hoje vou compartilhar algumas das escolhas de design que fizemos no Turbo Pack e como acredito que elas nos permitirão continuar a aprimorar o desempenho fantástico que já temos.
00:01:01Para ajudar a motivar isso,
00:01:03este é o nosso objetivo geral de design.
00:01:06A partir disso,
00:01:07vocês podem inferir imediatamente que provavelmente fizemos algumas escolhas difíceis.
00:01:14Então, e quanto às compilações a frio?
00:01:17Elas são importantes,
00:01:18mas uma das nossas ideias é que vocês não deveriam experimentá-las de forma alguma.
00:01:22E é nisso que esta palestra vai focar.
00:01:24Na palestra principal,
00:01:25vocês ouviram um pouco sobre como aproveitamos a incrementalidade para melhorar o desempenho do empacotamento.
00:01:31Uma ideia chave que temos para a incrementalidade é sobre cache.
00:01:35Queremos tornar tudo o que o bundler faz armazenável em cache,
00:01:38para que,
00:01:38sempre que vocês fizerem uma alteração,
00:01:40tenhamos que refazer apenas o trabalho relacionado a essa alteração.
00:01:43Ou,
00:01:43talvez,
00:01:44de outra forma,
00:01:45o custo da sua compilação deve realmente escalar com o tamanho ou a complexidade da sua alteração,
00:01:50em vez do tamanho ou da complexidade da sua aplicação.
00:01:53E é assim que podemos garantir que o Turbo Pack continuará a oferecer bom desempenho aos desenvolvedores,
00:01:58independentemente de quantas bibliotecas de ícones vocês importarem.
00:02:01Para ajudar a entender e motivar essa ideia,
00:02:04vamos imaginar o bundler mais simples do mundo,
00:02:07que talvez se pareça com isto.
00:02:09Então, aqui está o nosso bundler 'bebê'.
00:02:12E isso talvez seja um pouco de código demais para colocar em um slide,
00:02:16mas vai piorar.
00:02:17Então, aqui analisamos cada ponto de entrada.
00:02:20Seguimos suas importações,
00:02:21resolvemos suas referências,
00:02:23recursivamente por toda a aplicação para encontrar tudo de que vocês dependem.
00:02:28Então,
00:02:28no final,
00:02:29simplesmente coletamos tudo de que cada ponto de entrada depende e o colocamos em um arquivo de saída.
00:02:35Então, viva, temos um bundler 'bebê'.
00:02:38Obviamente,
00:02:39isso é ingênuo,
00:02:40mas se pensarmos do ponto de vista incremental,
00:02:43nenhuma parte disso é incremental.
00:02:45Então,
00:02:45definitivamente analisaremos certos arquivos várias vezes,
00:02:49talvez dependendo de quantas vezes vocês os importam,
00:02:52isso é terrível.
00:02:53Definitivamente resolveremos a importação do React centenas ou milhares de vezes.
00:02:57Então, sabe, que dor.
00:03:01Então,
00:03:01se quisermos que isso seja pelo menos um pouco mais incremental,
00:03:04precisamos encontrar uma maneira de evitar trabalho redundante.
00:03:08Então, vamos adicionar um cache.
00:03:10Então,
00:03:11vocês podem imaginar que esta é a nossa função de análise.
00:03:15É bem simples.
00:03:15E é provavelmente o 'cavalo de batalha' do nosso bundler.
00:03:19Sabe, muito simples.
00:03:19Lemos o conteúdo do arquivo,
00:03:22passamos para o SWC para nos dar uma AST.
00:03:25Então, vamos adicionar um cache.
00:03:27Ok, então isso é claramente uma vitória simples e agradável.
00:03:31Mas,
00:03:32sabe,
00:03:32tenho certeza de que alguns de vocês já escreveram código de cache antes.
00:03:36Talvez haja alguns problemas aqui.
00:03:38Tipo, sabe, e se o arquivo mudar?
00:03:41Isso é claramente algo com que nos importamos.
00:03:46E,
00:03:46sabe,
00:03:47e se o arquivo não for realmente um arquivo,
00:03:49mas três symlinks em um código de trincheira?
00:03:52Muitos gerenciadores de pacotes organizam dependências assim.
00:03:55E estamos usando o nome do arquivo como chave de cache.
00:03:59Isso é suficiente?
00:04:00Tipo,
00:04:00sabe,
00:04:01estamos empacotando para o cliente e para o servidor.
00:04:03Os mesmos arquivos acabam em ambos.
00:04:04Isso funciona?
00:04:05Também estamos armazenando a AST e a retornando.
00:04:08Então agora temos que nos preocupar com mutações.
00:04:11Então,
00:04:11sabe,
00:04:12e finalmente,
00:04:13esta não é uma maneira realmente ingênua de analisar?
00:04:16Sei que todos têm configurações massivas para o compilador.
00:04:21Tipo, parte disso tem que entrar aqui.
00:04:23Então, sim, tudo isso é um ótimo feedback.
00:04:27E esta é uma abordagem muito ingênua.
00:04:32E a isso, claro, eu diria, sim, isso não vai funcionar.
00:04:36Então o que fazemos para corrigir esses problemas?
00:04:39Por favor, corrijam e não cometam erros.
00:04:44Então, ok.
00:04:46Então, talvez isso seja um pouco melhor.
00:04:49Sabe, vocês podem ver aqui que temos algumas transformações.
00:04:52Precisamos fazer coisas personalizadas para cada arquivo,
00:04:55como talvez down-leveling ou implementar o uso de cache.
00:04:58Também temos alguma configuração.
00:05:00E então,
00:05:01claro,
00:05:01precisamos incluir isso em nossa chave para o nosso cache.
00:05:04Mas talvez de imediato vocês fiquem desconfiados.
00:05:08Tipo, isso está correto?
00:05:09Tipo,
00:05:10é realmente suficiente identificar uma transformação com base no nome?
00:05:13Não sei,
00:05:13talvez isso tenha uma configuração complicada própria.
00:05:16E,
00:05:17ok,
00:05:17e tipo,
00:05:18este valor JSON vai realmente capturar tudo o que nos importa?
00:05:24Os desenvolvedores vão mantê-lo?
00:05:26Quão grandes serão essas chaves de cache?
00:05:29Quantas cópias da configuração teremos?
00:05:31Então,
00:05:32eu pessoalmente já vi código exatamente assim,
00:05:34e acho quase impossível de raciocinar sobre ele.
00:05:37Ok,
00:05:37também tentamos corrigir este outro problema em torno das invalidações.
00:05:43Então adicionamos uma API de callback para ler o arquivo.
00:05:46Isso é ótimo,
00:05:47então se o arquivo mudar,
00:05:48podemos simplesmente removê-lo do cache,
00:05:51para não continuarmos a servir conteúdo obsoleto.
00:05:55Ok,
00:05:55mas isso é bastante ingênuo,
00:05:57porque,
00:05:57claro,
00:05:58precisamos 'limpar' nosso cache,
00:05:59mas quem nos chamou também precisa saber que precisa obter uma nova cópia.
00:06:03Então, ok, vamos começar a encadear callbacks.
00:06:06Ok, conseguimos.
00:06:09Encadeamos callbacks pela pilha.
00:06:12Vocês podem ver aqui que permitimos que nosso chamador se inscreva em mudanças.
00:06:16Podemos simplesmente rodar novamente o bundle inteiro se algo mudar,
00:06:20e se um arquivo mudar,
00:06:21nós o chamamos.
00:06:22Ótimo, temos um bundler reativo.
00:06:25Mas isso ainda é dificilmente incremental.
00:06:28Então,
00:06:29se um arquivo muda,
00:06:30precisamos percorrer todos os módulos novamente e produzir todos os arquivos de saída.
00:06:37Então,
00:06:38sabe,
00:06:38economizamos muito trabalho com nosso cache de análise,
00:06:43mas isso não é realmente suficiente.
00:06:45E então, finalmente, há todo esse outro trabalho redundante.
00:06:49Tipo,
00:06:49definitivamente queremos armazenar as importações em cache.
00:06:52Podemos encontrar um arquivo várias vezes,
00:06:53e continuamos precisando de suas importações,
00:06:55então queremos colocar um cache lá.
00:06:57E,
00:06:58sabe,
00:06:58os resultados de resolução são realmente bem complicados,
00:07:01então devemos definitivamente armazená-los em cache para que possamos reutilizar o trabalho que fizemos resolvendo o React.
00:07:08Mas, ok, agora temos outro problema.
00:07:11Seus resultados de resolução mudam quando vocês atualizam dependências ou adicionam novos arquivos,
00:07:16então precisamos de outro callback lá.
00:07:18E definitivamente também queremos,
00:07:19tipo,
00:07:20armazenar em cache a lógica para produzir saídas,
00:07:23porque se vocês pensarem em uma sessão HMR,
00:07:25vocês estão editando uma parte da aplicação,
00:07:28então por que estamos reescrevendo todas as saídas toda vez?
00:07:31E também,
00:07:32vocês podem,
00:07:32tipo,
00:07:33deletar um arquivo de saída,
00:07:35então provavelmente deveríamos ouvir as mudanças lá também.
00:07:39Ok,
00:07:39então talvez tenhamos resolvido todas essas coisas,
00:07:43mas ainda temos este problema,
00:07:45que é: toda vez que algo muda,
00:07:47começamos do zero.
00:07:48Então,
00:07:48meio que todo o fluxo de controle desta função não funciona porque,
00:07:52se um único arquivo muda,
00:07:53nós realmente gostaríamos de pular para o meio daquele loop 'for'.
00:07:56E então,
00:07:57finalmente,
00:07:58nossa API para quem nos chama também é irremediavelmente ingênua.
00:08:03Eles provavelmente querem saber qual arquivo mudou,
00:08:05para que possam,
00:08:05tipo,
00:08:06enviar atualizações para o cliente.
00:08:07Então, sim.
00:08:11Então, essa abordagem realmente não funciona.
00:08:13E mesmo que de alguma forma tivéssemos encadeado todos os callbacks em todos esses lugares,
00:08:18vocês acham que conseguiriam realmente manter este código?
00:08:21Vocês acham que poderiam,
00:08:22tipo,
00:08:23adicionar uma nova funcionalidade a ele?
00:08:24Eu não.
00:08:25Acho que isso simplesmente falharia e daria errado.
00:08:28E, sabe, a isso, eu diria, sim.
00:08:34Então, mais uma vez, o que devemos fazer?
00:08:36Sabe,
00:08:36assim como quando vocês estão conversando com um LLM,
00:08:40vocês precisam primeiro saber o que querem.
00:08:43E então vocês têm que ser extremamente claros sobre isso.
00:08:48Então, o que nós realmente queremos?
00:08:50Então,
00:08:50sabe,
00:08:51consideramos muitas abordagens diferentes,
00:08:53e muitas pessoas na equipe realmente tinham muita experiência trabalhando com bundlers.
00:08:59Então, chegamos a esses tipos de requisitos básicos.
00:09:02Então,
00:09:02definitivamente queremos ser capazes de armazenar em cache cada operação cara no bundler.
00:09:05E deveria ser muito fácil fazer isso.
00:09:08Tipo,
00:09:09vocês não deveriam receber 15 comentários na revisão de código toda vez que adicionam um novo cache.
00:09:12E então eu realmente não confio nos desenvolvedores para escrever chaves de cache corretas ou rastrear entradas ou rastrear dependências manualmente.
00:09:24Então, nós deveríamos lidar com isso.
00:09:26Definitivamente deveríamos tornar isso à prova de falhas.
00:09:30Em seguida, precisamos lidar com a mudança de entradas.
00:09:33Esta é uma grande ideia no HMR, mas mesmo entre sessões.
00:09:36Então,
00:09:36principalmente serão arquivos,
00:09:38mas também podem ser coisas como configurações.
00:09:40E com o cache do sistema de arquivos,
00:09:42acaba sendo também coisas como variáveis de ambiente.
00:09:45Então, queremos ser reativos.
00:09:47Queremos ser capazes de recalcular as coisas assim que algo mudar,
00:09:51e não queremos encadear callbacks em todos os lugares.
00:09:54Finalmente,
00:09:55precisamos apenas aproveitar as arquiteturas modernas e ser multi-threaded e,
00:10:01em geral,
00:10:02rápidos.
00:10:02Então,
00:10:03talvez vocês estejam olhando para este conjunto de requisitos,
00:10:07e alguns de vocês estejam pensando,
00:10:09o que isso tem a ver com um bundler?
00:10:12E a isso,
00:10:12eu diria,
00:10:13claro,
00:10:13minha equipe de gestão está na sala,
00:10:16então não precisamos realmente falar sobre isso.
00:10:20Mas,
00:10:21na verdade,
00:10:21estou imaginando que muitos de vocês chegaram à conclusão muito mais óbvia.
00:10:24Isso soa muito como 'signals'.
00:10:28E sim, estou descrevendo um sistema que soa como 'signals'.
00:10:31É uma forma de compor cálculos,
00:10:33rastrear dependências,
00:10:34com alguma quantidade de memoização automática.
00:10:37E devo notar que nos inspiramos em todos os tipos de sistemas,
00:10:41especialmente no compilador Rust e em um sistema chamado Salsa.
00:10:45E há até uma literatura acadêmica sobre esses conceitos chamada Adaptons,
00:10:49se vocês estiverem interessados.
00:10:51Ok,
00:10:51então vamos dar uma olhada no que,
00:10:53vamos ver como isso funciona na prática,
00:10:56e então faremos um salto muito abrupto de exemplos de código em JavaScript para Rust.
00:11:01Então,
00:11:01aqui está um exemplo da infraestrutura que construímos.
00:11:05Uma função TurboTask é uma unidade de trabalho em cache em nosso compilador.
00:11:12Então,
00:11:13uma vez que vocês anotam uma função assim,
00:11:16podemos rastreá-la,
00:11:17construir uma chave de cache a partir de seus parâmetros,
00:11:21e isso nos permite tanto armazená-la em cache quanto reexecutá-la quando necessário.
00:11:28Esses tipos VC aqui,
00:11:29vocês podem pensar neles como 'signals',
00:11:32este é um valor reativo,
00:11:34VC significa 'value cell',
00:11:35mas 'signal' pode ser um nome um pouco melhor.
00:11:39Quando vocês declaram um parâmetro assim,
00:11:42estão dizendo que isso pode mudar,
00:11:45quero reexecutar quando mudar.
00:11:47E como sabemos disso?
00:11:49Então lemos esses valores através de um 'await'.
00:11:52Uma vez que vocês esperam por um valor reativo como este,
00:11:55rastreamos automaticamente a dependência.
00:11:58E então,
00:11:58finalmente,
00:11:59claro,
00:12:00fazemos o cálculo real que queríamos fazer,
00:12:04e o armazenamos em uma célula.
00:12:07Então,
00:12:07como rastreamos automaticamente as dependências,
00:12:11sabemos que esta função depende tanto do conteúdo do arquivo quanto do valor da configuração.
00:12:17E toda vez que armazenamos um novo resultado na célula,
00:12:21podemos compará-lo com o anterior,
00:12:23e então,
00:12:23se ele mudou,
00:12:24podemos propagar notificações para todos que leram esse valor.
00:12:29Então,
00:12:29este conceito de mudança é fundamental para nossa abordagem de incrementalidade.
00:12:33E sim, novamente, o caso mais simples está bem aqui.
00:12:37Se o arquivo mudar,
00:12:38o Turbo Pack observará isso,
00:12:40invalidará a execução desta função e a reexecutará imediatamente.
00:12:45E então,
00:12:45se por acaso produzirmos a mesma AST,
00:12:48pararemos ali mesmo porque calculamos a mesma célula.
00:12:53Agora,
00:12:54para analisar um arquivo,
00:12:55dificilmente há qualquer edição que vocês possam fazer que não altere a AST.
00:13:00Mas podemos aproveitar a composibilidade fundamental das funções do Turbo Pack para levar isso adiante.
00:13:07Então aqui,
00:13:08vemos outra função de cache do Turbo Pack extraindo importações de um módulo.
00:13:15Vocês podem imaginar que esta é uma tarefa muito comum que temos no Bundler.
00:13:20Precisamos extrair importações apenas para realmente encontrar todos os módulos em sua aplicação.
00:13:25Nós os aproveitamos para escolher a melhor forma de agrupar módulos em 'chunks'.
00:13:29E,
00:13:29claro,
00:13:30o grafo de importação é importante para tarefas básicas como 'tree shaking'.
00:13:34E como há tantos consumidores diferentes dos dados de importação,
00:13:39um cache faz muito sentido.
00:13:41Então, esta implementação não é realmente especial.
00:13:44Isso é o que vocês encontrariam em qualquer tipo de Bundler.
00:13:46Percorremos a AST,
00:13:47coletamos as importações em alguma estrutura de dados especial que gostamos e então as retornamos.
00:13:55Mas a ideia chave aqui é que os armazenamos em outra célula.
00:13:58Então,
00:13:58se o módulo muda,
00:14:00precisamos reexecutar esta função porque a lemos.
00:14:05Mas se vocês pensarem no tipo de mudanças que fazem nos módulos,
00:14:09muito poucas delas realmente afetam as importações.
00:14:12Então vocês mudam o módulo,
00:14:14atualizam o corpo da função,
00:14:16um literal de string,
00:14:17qualquer tipo de detalhe de implementação.
00:14:20Isso invalidará esta função e então calcularemos o mesmo conjunto de importações.
00:14:25E então não invalidamos nada que tenha lido isso.
00:14:29Então,
00:14:29se vocês pensarem nisso em uma sessão HMR,
00:14:32isso significa que precisamos reanalisar seu arquivo,
00:14:35mas realmente não precisamos mais pensar em como tomar decisões de 'chunking'.
00:14:40Não precisamos pensar em nenhum tipo de resultado de 'tree shaking' porque sabemos que eles não mudaram.
00:14:45Então podemos pular imediatamente da análise do arquivo,
00:14:48fazendo esta análise simples,
00:14:50e então pular direto para a produção de saídas.
00:14:53E esta é uma das maneiras pelas quais temos tempos de atualização realmente rápidos.
00:14:57Então isso é bastante imperativo.
00:15:02Outra maneira de pensar sobre esta ideia básica é como um grafo de nós.
00:15:06Então,
00:15:07aqui à esquerda,
00:15:08vocês podem imaginar uma compilação a frio.
00:15:12Inicialmente,
00:15:12realmente temos que ler cada arquivo,
00:15:14analisar todos eles,
00:15:15analisar todas as importações.
00:15:17E como efeito colateral disso,
00:15:18coletamos todas as informações de dependência da sua aplicação.
00:15:21E então,
00:15:22quando algo muda,
00:15:23podemos aproveitar esse grafo de dependência que construímos para propagar invalidações,
00:15:29subir a pilha e reexecutar as funções do Turbo Pack.
00:15:32E então, se eles produzem um novo valor, paramos por aí.
00:15:35Caso contrário, continuamos propagando a invalidação.
00:15:37Então, ótimo.
00:15:41Sabe,
00:15:41isso é na verdade uma super simplificação massiva do que estamos fazendo na prática,
00:15:46vocês podem imaginar.
00:15:47Então,
00:15:48no Turbo Pack hoje,
00:15:49existem cerca de 2.500 funções Turbo task diferentes.
00:15:53E em uma compilação típica,
00:15:54podemos ter literalmente milhões de tarefas diferentes.
00:15:58Então, na verdade, parece talvez um pouco mais com isso.
00:16:01Agora, eu realmente não espero que vocês consigam ler isso.
00:16:04Não consegui realmente encaixar no slide.
00:16:06Então, talvez devêssemos diminuir o zoom.
00:16:08Ok, então isso não é obviamente útil.
00:16:14Na realidade,
00:16:15temos maneiras melhores de rastrear e visualizar o que está acontecendo dentro do Turbo Pack.
00:16:21Mas,
00:16:22fundamentalmente,
00:16:22esses trabalhos descartam a vasta maioria das informações de dependência.
00:16:26E agora estou imaginando que alguns de vocês talvez realmente tenham experiência trabalhando com 'signals',
00:16:33talvez más experiências.
00:16:34Sabe,
00:16:35eu,
00:16:35por exemplo,
00:16:36gosto de rastreamentos de pilha e de poder entrar e sair de funções em um depurador.
00:16:41Então,
00:16:41talvez vocês estejam desconfiados de que esta seja a panaceia completa.
00:16:45Tipo, obviamente vem com compensações.
00:16:47E sim,
00:16:48e a isso eu diria,
00:16:50bem,
00:16:50sabe,
00:16:51o que eu realmente diria é que toda a engenharia de software é sobre gerenciar compensações.
00:17:01Nem sempre estamos resolvendo problemas exatamente,
00:17:04mas estamos realmente escolhendo novos conjuntos de compensações para entregar valor.
00:17:08Então,
00:17:09para alcançar nossos objetivos de design em torno de compilações incrementais no Turbo Pack,
00:17:14apostamos todas as nossas fichas neste modelo de programação reativa incremental.
00:17:19E isso, claro, teve algumas consequências muito naturais.
00:17:23Então,
00:17:24sabe,
00:17:24talvez realmente tenhamos resolvido o problema de sistemas de cache feitos à mão e lógica de invalidação complicada.
00:17:33Em troca,
00:17:34temos que gerenciar uma infraestrutura de cache complicada.
00:17:39E claro, sabe, isso me parece uma troca muito boa.
00:17:42Eu gosto de infraestrutura de cache complicada,
00:17:44mas todos nós temos que viver com as consequências.
00:17:48Então,
00:17:48o primeiro,
00:17:49claro,
00:17:50são apenas os custos gerais centrais deste sistema.
00:17:54Sabe,
00:17:55se vocês pensarem bem,
00:17:56em uma determinada compilação ou sessão HMR,
00:18:00vocês não estão realmente mudando muita coisa.
00:18:04Então,
00:18:04rastreamos todas as informações de dependência entre cada importação e cada resultado de resolução em sua aplicação,
00:18:11mas vocês só vão realmente mudar algumas delas.
00:18:13Então,
00:18:13a maior parte das informações de dependência que coletamos nunca é realmente necessária.
00:18:16Então,
00:18:17sabe,
00:18:17para gerenciar isso,
00:18:19tivemos que focar muito em impulsionar,
00:18:21em melhorar o desempenho desta camada de cache para reduzir os custos e permitir que nosso sistema escalasse para aplicações cada vez maiores.
00:18:30E o próximo e mais óbvio é simplesmente a memória.
00:18:34Sabe,
00:18:35caches são sempre fundamentalmente uma compensação entre tempo e memória.
00:18:38E o nosso não faz nada de diferente nisso.
00:18:41Nosso objetivo simples é que o tamanho do cache deve escalar linearmente com o tamanho da sua aplicação.
00:18:49Mas, novamente, temos que ter cuidado com os custos gerais.
00:18:51Este próximo é um pouco sutil.
00:18:54Então,
00:18:55temos muitos algoritmos no bundler,
00:18:57como vocês podem esperar.
00:18:58E alguns deles meio que exigem entender algo global sobre sua aplicação.
00:19:03Bem,
00:19:03isso é um problema porque,
00:19:04sempre que vocês dependem de informações globais,
00:19:07isso significa que qualquer mudança pode invalidar essa operação.
00:19:10Então,
00:19:11temos que ter cuidado com a forma como projetamos esses algoritmos,
00:19:14compondo as coisas cuidadosamente para que possamos preservar a incrementalidade.
00:19:17E, finalmente, este é talvez um pouco de uma queixa pessoal.
00:19:24Tudo é assíncrono no Turbo Pack.
00:19:27E isso é ótimo para escalabilidade horizontal,
00:19:29mas,
00:19:29mais uma vez,
00:19:30prejudica nossos objetivos fundamentais de,
00:19:32sabe,
00:19:32depuração e perfil de desempenho.
00:19:38Então,
00:19:38tenho certeza de que muitos de vocês têm experiência depurando assíncrono em ferramentas de desenvolvedor como as do Chrome.
00:19:46E esta é geralmente uma experiência bastante agradável.
00:19:48Nem sempre ideal.
00:19:49E garanto que Rust com LLDB está a anos-luz de distância.
00:19:53Então,
00:19:54para gerenciar isso,
00:19:55tivemos que investir em ferramentas personalizadas de visualização,
00:19:59instrumentação e rastreamento.
00:20:01E olhem só,
00:20:02mais um projeto de infraestrutura que não é um bundler.
00:20:07Ok,
00:20:07então vamos dar uma olhada e ver se fizemos a aposta certa.
00:20:11Então,
00:20:11na Vercel,
00:20:12temos uma aplicação de produção muito grande.
00:20:17Achamos que é talvez uma das maiores do mundo,
00:20:19mas,
00:20:19sabe,
00:20:20não sabemos realmente.
00:20:21Mas ela tem cerca de 80.000 módulos.
00:20:23Então,
00:20:24vamos dar uma olhada em como o Turbo Pack se sai nela.
00:20:26Para o 'fast refresh',
00:20:28realmente dominamos o que o Web Pack é capaz de entregar.
00:20:32Mas isso é meio que notícia velha.
00:20:33O Turbo Pack para desenvolvimento já está disponível há algum tempo,
00:20:36e eu realmente espero que todos estejam pelo menos usando-o em desenvolvimento.
00:20:39Mas,
00:20:39sabe,
00:20:40a novidade aqui hoje,
00:20:41claro,
00:20:41é que as compilações estão estáveis.
00:20:42Então, vamos olhar para uma compilação.
00:20:44E aqui vocês podem ver uma vitória substancial sobre o Web Pack para esta aplicação.
00:20:49Esta compilação em particular está realmente rodando com nossa nova camada experimental de cache do sistema de arquivos.
00:20:53Então,
00:20:54cerca de 16 desses 94 segundos são apenas para descarregar o cache no final.
00:20:59E isso é algo que vamos trabalhar para melhorar à medida que o cache do sistema de arquivos se tornar estável.
00:21:04Mas,
00:21:04claro,
00:21:05a questão das compilações a frio é que elas são 'frias',
00:21:07nada é incremental.
00:21:07Então, vamos dar uma olhada em uma compilação 'quente' real.
00:21:10Então,
00:21:11usando o cache da compilação a frio,
00:21:13podemos ver isso.
00:21:14Então, isso é apenas uma espiada em onde estamos hoje.
00:21:17Como temos este sistema de cache granular,
00:21:19podemos simplesmente gravar o cache no disco e,
00:21:22na próxima compilação,
00:21:23lê-lo de volta,
00:21:23descobrir o que mudou e finalizar a compilação.
00:21:26Ok,
00:21:27então isso parece muito bom,
00:21:28mas muitos de vocês estão pensando,
00:21:30bem,
00:21:31talvez eu pessoalmente não tenha a maior aplicação Next.js do mundo.
00:21:34Então, vamos dar uma olhada em um exemplo menor.
00:21:37O site react.dev é um pouco menor.
00:21:41Também é meio interessante porque é um compilador React.
00:21:44Não surpreendentemente,
00:21:45é um dos primeiros a adotar o compilador React.
00:21:47E o compilador React é implementado em Babel.
00:21:49E isso é meio que um problema para nossa abordagem porque significa que para cada arquivo na aplicação,
00:21:53precisamos pedir ao Babel para processá-lo.
00:21:55Então,
00:21:55e fundamentalmente,
00:21:57eu diria que nós,
00:21:58ou eu,
00:21:58não podemos tornar o compilador React mais rápido.
00:22:01Não é meu trabalho.
00:22:02Meu trabalho é o Turbo Pack.
00:22:03Mas podemos descobrir exatamente quando chamá-lo.
00:22:07Então,
00:22:08olhando para os tempos de 'fast refresh',
00:22:10fiquei um pouco desapontado com este resultado.
00:22:13E acontece que cerca de 130 desses 140 milissegundos são do compilador React.
00:22:18E tanto o Turbo Pack quanto o Web Pack estão fazendo isso.
00:22:22Mas com o Turbo Pack,
00:22:23podemos,
00:22:23depois que o compilador React processar essa mudança,
00:22:27ver,
00:22:27ah,
00:22:28as importações não mudaram.
00:22:29Joguem-no na saída e continuem.
00:22:31Mais uma vez,
00:22:32em compilações a frio,
00:22:33vemos esse tipo de vitória consistente de 3x.
00:22:37E só para deixar claro, isso é na minha máquina.
00:22:39Mas,
00:22:40novamente,
00:22:41nenhuma incrementalidade em uma compilação a frio.
00:22:44E em uma compilação 'quente', vemos um tempo muito melhor.
00:22:47Então,
00:22:47novamente,
00:22:48com uma compilação 'quente',
00:22:50já temos o cache no disco.
00:22:52Tudo o que precisamos fazer é basicamente,
00:22:54uma vez que começamos,
00:22:55descobrir quais arquivos na aplicação mudaram,
00:22:57reexecutar esses trabalhos e então reutilizar todo o resto da compilação anterior.
00:23:01Então, a pergunta básica é: já somos Turbo?
00:23:05Sim.
00:23:06Então sim, isso foi discutido na palestra principal, claro.
00:23:09O Turbo Pack está estável a partir do Next 16.
00:23:12E somos até o bundler padrão para o Next.
00:23:14Então, sabe, missão cumprida, de nada.
00:23:17Mas. (risos) (plateia aplaude)
00:23:23E se vocês notaram aquela coisa de 'revert' na palestra principal,
00:23:27era eu tentando tornar o Turbo Pack o padrão.
00:23:30Levou apenas três tentativas.
00:23:31Mas o que eu realmente quero deixar com vocês,
00:23:34novamente,
00:23:35é isto.
00:23:35Sabe, porque não terminamos.
00:23:37Ainda temos muito a fazer em desempenho,
00:23:39e finalizar a transição para a camada de cache do sistema de arquivos.
00:23:42Sugiro que todos experimentem em desenvolvimento.
00:23:44E é isso.
00:23:46Muito obrigado.
00:23:47Por favor, me procurem, façam perguntas.
00:23:49(plateia aplaude) (música animada) (música animada)

Key Takeaway

O Turbo Pack da Vercel revoluciona o empacotamento de aplicações web através de um modelo de programação reativa incremental e um sistema de cache inteligente, resultando em compilações e atualizações significativamente mais rápidas, embora com a complexidade de gerenciar uma infraestrutura de cache avançada.

Highlights

O Turbo Pack utiliza um modelo de programação reativa incremental para otimizar o desempenho do bundler, focando em cachear cada operação cara.

O sistema automatiza o rastreamento de dependências e a invalidação, garantindo que o custo da compilação escale com a complexidade da alteração, não com o tamanho da aplicação.

A abordagem reativa, inspirada em 'signals' e compiladores como Rust, permite reexecutar apenas as partes necessárias do código quando há mudanças.

Resultados de desempenho demonstram que o Turbo Pack supera significativamente o Webpack em tempos de 'fast refresh', compilações a frio e a quente, mesmo em aplicações de grande escala.

Embora resolva a complexidade de caches manuais, o modelo introduz desafios como a gestão de uma infraestrutura de cache complexa e a dificuldade de depuração assíncrona.

O Turbo Pack é agora o bundler padrão para o Next.js a partir da versão 16, com planos contínuos para melhorias de desempenho e finalização da transição para o cache de sistema de arquivos.

Timeline

Introdução ao Turbo Pack e Experiência na Vercel

Luke Sandberg, engenheiro de software na Vercel, apresenta-se e seu trabalho no Turbo Pack. Ele compartilha sua experiência anterior no Google, onde trabalhou com cadeias de ferramentas web internas e compiladores, contrastando com a abordagem da Vercel. A palestra visa detalhar as escolhas de design do Turbo Pack e como elas contribuem para seu desempenho. Este segmento estabelece o contexto e a credibilidade do palestrante, preparando o público para os conceitos técnicos que serão abordados.

Objetivo de Design: Incrementalidade e Cache

O principal objetivo de design do Turbo Pack é aprimorar o desempenho através da incrementalidade, minimizando a experiência de 'compilações a frio'. A ideia central é tornar todas as operações do bundler armazenáveis em cache, garantindo que apenas o trabalho relacionado a uma alteração seja refeito. Isso significa que o custo da compilação deve escalar com a complexidade da mudança, e não com o tamanho total da aplicação. O palestrante ilustra a ineficiência de um 'bundler bebê' ingênuo, que realiza trabalho redundante ao analisar e resolver dependências múltiplas vezes.

Desafios da Implementação de Cache Ingênuo

Luke explora as dificuldades de adicionar um cache simples a um bundler, destacando problemas como a invalidação de cache quando arquivos mudam ou o uso de symlinks. Ele aponta que usar apenas o nome do arquivo como chave de cache é insuficiente e que a inclusão de configurações complexas torna as chaves de cache difíceis de gerenciar. A tentativa de resolver invalidações com callbacks leva a uma cadeia de chamadas complexa que ainda resulta na reexecução de grande parte do bundle. O palestrante conclui que essa abordagem é insustentável e não oferece a incrementalidade desejada.

A Solução: Modelo de Programação Reativa com 'Signals'

Para superar os desafios, o Turbo Pack adota um modelo de programação reativa inspirado em 'signals', compiladores como Rust e sistemas como Salsa. Este modelo permite cachear cada operação cara, automatizar a geração de chaves de cache e o rastreamento de dependências, e reagir a mudanças de forma eficiente sem callbacks encadeados. Funções `TurboTask` são unidades de trabalho cacheáveis, e `VC` (Value Cells) representam valores reativos que, quando 'awaitados', rastreiam automaticamente as dependências. Isso permite que o sistema reexecute apenas as partes necessárias do grafo de dependências, otimizando drasticamente as atualizações.

Compensações e Desafios do Sistema Reativo

Embora o modelo reativo resolva a complexidade de caches manuais, ele introduz novas compensações. O sistema exige o gerenciamento de uma infraestrutura de cache complexa, com custos gerais significativos devido ao rastreamento de dependências, mesmo para informações que raramente mudam. Há também uma troca entre tempo e memória, onde o tamanho do cache deve escalar linearmente com a aplicação. Além disso, algoritmos que dependem de informações globais precisam ser cuidadosamente projetados para preservar a incrementalidade, e a natureza assíncrona do Turbo Pack, embora ótima para escalabilidade, dificulta a depuração e o perfil de desempenho, exigindo ferramentas personalizadas.

Resultados de Desempenho e Futuro do Turbo Pack

O palestrante apresenta dados de desempenho do Turbo Pack em aplicações reais, incluindo a grande aplicação de produção da Vercel (80.000 módulos) e o site react.dev. O Turbo Pack demonstra vitórias substanciais sobre o Webpack em 'fast refresh', compilações a frio e, especialmente, compilações a quente, onde o cache de disco reduz os tempos para segundos ou milissegundos. Apesar de gargalos externos como o compilador React baseado em Babel, o Turbo Pack otimiza quando essas ferramentas são chamadas. A conclusão é que o Turbo Pack já está 'Turbo', sendo estável e o bundler padrão para Next.js 16, com planos contínuos para aprimoramento e transição para o cache de sistema de arquivos.

Community Posts

View all posts