Hardwares

Por que a memória se tornou algo tão importante nos PCs atuais?

Desempenho de CPUs, GPUs e SSDs depende mais da forma como os dados são acessados e movimentados que da velocidade bruta dos componentes.
Imagem de: Por que a memória se tornou algo tão importante nos PCs atuais?

Durante décadas, era relativamente simples resumir o desempenho de um computador em algumas especificações principais.

CPUs eram vendidas com foco em frequência e número de núcleos, GPUs em poder de processamento e, depois, teraFLOPS, enquanto memórias eram comparadas pela frequência e unidades de armazenamento pela velocidade de leitura e gravação sequencial.

Esses números continuam sendo parte fundamental, mas os processadores atuais ficaram tão rápidos em cálculos que outro fator passou a ter um peso cada vez maior no desempenho real: levar os dados ao lugar certo no momento em que eles são necessários.

Um núcleo moderno de CPU consegue executar várias instruções a cada ciclo de clock. Uma GPU de alto desempenho reúne milhares de unidades de cálculo trabalhando em paralelo.

Aceleradores de inteligência artificial nos dias de hoje já trabalham com multiplicações de matrizes na escala de petaFLOPS.

Nada disso serve, porém, se essas unidades não receberem operandos, instruções, texturas, geometria, pesos de modelos, dados intermediários ou qualquer outra informação necessária para o processamento.

Existe, portanto, um desequilíbrio importante. A capacidade de cálculo cresceu muito ao longo dos anos, enquanto movimentar dados continua custando caro em latência, largura de banda, energia e espaço físico dentro dos chips.

Por isso, muitos programas atuais gastam boa parte do tempo apenas tentando manter as unidades de execução abastecidas. Isso não significa que todo programa seja limitado pela memória.

O desempenho também pode ficar preso à capacidade de cálculo, dependências entre instruções, previsão de desvios, sincronização, sobrecarga de software, armazenamento, rede e vários outros fatores.

A diferença é que entender o comportamento da memória se tornou essencial para compreender o desempenho dos computadores atuais. E desempenho de memória vai muito além de frequência ou capacidade.

Latência, largura de banda, localidade, tamanho dos caches, paralelismo entre acessos, padrão de leitura e gravação, prefetch, topologia das interconexões e até a localização física dos dados trabalham em conjunto.

Para entender o por quê disso tudo, é preciso começar pela forma como esses dados são acessados. E é exatamente isso que vamos abordar na próxima seção deste artigo.

Acesso sequencial e aleatório podem se comportar como cargas totalmente diferentes

Imagine a leitura de um arquivo de 1 GB do começo ao fim. A posição do próximo bloco de dados é totalmente previsível.

Assim que o sistema identifica esse padrão, ele pode começar a buscar informações futuras antes mesmo de elas serem solicitadas diretamente.

Esse é um padrão de acesso sequencial, algo que os computadores modernos executam muito bem. CPUs possuem mecanismos de prefetch em hardware criados para reconhecer fluxos previsíveis de memória.

Controladores de DRAM conseguem manter várias transações em andamento e reorganizar solicitações. Controladores de SSD distribuem acessos entre diferentes dies e canais de NAND. GPUs agrupam requisições feitas por threads próximas em transações maiores.

Na prática, o hardware consegue montar uma espécie de fluxo contínuo de dados. O acesso aleatório é bem mais difícil. Pense em uma lista encadeada na qual cada elemento guarda o endereço do próximo.

O processador pode não saber qual será o próximo acesso até concluir a leitura atual. Isso reduz bastante as oportunidades de prefetch e dificulta a execução de vários acessos ao mesmo tempo.

Por esse motivo, duas cargas de trabalho podem movimentar exatamente a mesma quantidade de dados e ainda apresentar velocidades completamente diferentes.

A diferença aumenta quando existem dependências de latência. Se uma CPU solicita quatro linhas de cache sem relação entre si, ela pode manter várias falhas de cache em andamento ao mesmo tempo e esconder parte da espera.

Se a solicitação B depender do conteúdo retornado pela solicitação A, B só poderá começar quando A terminar. Esse comportamento costuma ser demonstrado com o chamado pointer chasing.

Cada leitura revela o endereço da próxima, fazendo o processador pagar uma parcela muito maior da latência real da memória em vez de escondê-la com especulação ou paralelismo.

O mesmo princípio aparece em várias áreas da computação. Um banco de dados fazendo pequenas buscas imprevisíveis apresenta um problema completamente diferente de uma cópia de arquivo grande.

Uma GPU lendo texels próximos e coerentes trabalha de forma diferente de milhares de threads acessando endereços sem relação entre si.

Um SSD lendo centenas de kilobytes em sequência também recebe uma carga muito diferente daquela formada por blocos de 4 KB espalhados pela unidade.

Isso também explica por que não basta transformar qualquer acesso aleatório em sequencial. Desenvolvedores tentam fazer isso sempre que possível. Bancos de dados reorganizam informações.

Motores de jogos agrupam tarefas. Algoritmos de GPU reordenam solicitações. Compiladores modificam a organização dos dados. Estruturas do tipo arrays-of-structures podem ser substituídas por structures-of-arrays.

A ordenação também pode transformar acessos espalhados em sequências mais coerentes. Só que nem sempre existe essa opção. Se o próximo endereço depende do conteúdo recebido na operação atual, a dependência é real.

Se um raio pode seguir para uma área imprevisível de uma cena 3D, a GPU não consegue conhecer todos os acessos futuros com antecedência.

Se um usuário de banco de dados solicita um registro específico, ler primeiro um terabyte de registros vizinhos acabaria com a utilidade da consulta.

O objetivo passa a ser reduzir a frequência desses acessos caros e torná-los mais previsíveis e paralelos sempre que possível.

Acesso Aleatório vs Sequencial
O acesso sequencial lê os dados vizinhos em ordem, permitindo uma pré-busca eficiente e alta taxa de transferência. O acesso aleatório salta entre locais não relacionados, dificultando a previsão e aumentando a latência. Fonte da imagem: Wikipedia

A localidade explica como os caches funcionam

Programas reais raramente acessam dados totalmente aleatórios o tempo inteiro. Em geral, eles apresentam dois comportamentos importantes: localidade espacial e localidade temporal.

Localidade espacial significa que, quando um programa acessa determinado dado, existe uma boa chance de ele precisar de dados próximos logo depois.

Localidade temporal significa que um dado usado recentemente tem uma chance razoável de ser usado novamente em pouco tempo. Um loop processando um array possui ótima localidade espacial.

Informações de um mundo de jogo acessadas com frequência podem ter boa localidade temporal. Instruções dentro de um hot loop, uma parte do código executada repetidas vezes, apresentam os dois tipos de localidade.

É isso que torna os caches tão úteis. Quando uma CPU precisa de um byte armazenado na DRAM, ela normalmente não transfere apenas aquele byte. Os dados passam pela hierarquia de cache em blocos de tamanho fixo chamados linhas de cache.

Em processadores x86 comuns, essas linhas geralmente possuem 64 bytes. A própria documentação de otimização da AMD trata da organização e do acesso a dados considerando linhas de cache de 64 bytes.

Se o programa acessar dados vizinhos logo depois, eles já podem estar no cache. Isso leva a outro conceito importante: o working set, ou conjunto de dados ativo.

Um programa pode reservar dezenas de gigabytes de memória e, durante determinada etapa, trabalhar repetidamente com apenas uma pequena parte desse espaço. Se esse conjunto ativo couber em um cache rápido, o desempenho pode ser excelente.

Basta aumentar esse conjunto um pouco além da capacidade disponível para que muito mais acessos precisem seguir para um nível mais lento da hierarquia.

A mudança de desempenho pode ser bastante brusca. É por isso que aumentar o cache pode trazer ganhos enormes em alguns programas e quase nenhum em outros.

Passar de 32 MB para 96 MB de cache pode fazer muita diferença se um conjunto de 60 MB usado repetidamente conseguir permanecer dentro do chip.

O benefício será bem menor caso o programa percorra vários gigabytes de dados uma única vez e não volte a usá-los. Caches funcionam melhor quando o software reutiliza aquilo que foi armazenado.

Localidade Espacial vs Temporal
A localidade espacial refere-se ao acesso a locais de memória próximos em rápida sucessão, enquanto a localidade temporal refere-se à reutilização dos mesmos dados em um curto período de tempo. Ambos os comportamentos permitem que os caches atendam a mais solicitações sem acessar repetidamente a memória principal, que é muito mais lenta.

Por que CPUs usam vários níveis de cache em vez de um único cache enorme?

Se cache é algo tão útil, é natural que apareçam perguntas como: por que não construir um cache gigantesco e muito rápido e abandonar as memórias mais lentas?

E a resposta é mais simples do que parece. Porque as características desejadas em uma memória entram em conflito umas com as outras.

Os caches mais próximos da CPU usam SRAM e são projetados para oferecer latência muito baixa e largura de banda muito alta. O problema é que SRAM consome bastante área de silício.

Estruturas maiores também precisam de mais fios, caminhos físicos mais longos, circuitos adicionais de busca e, muitas vezes, mais energia por acesso.

Quanto maior a capacidade, mais difícil fica manter a velocidade de um pequeno cache local. É por isso que existe uma hierarquia.

O cache L1 é pequeno o suficiente para ficar muito próximo das unidades de execução de cada núcleo e atender solicitações com baixa latência. O L2 oferece mais capacidade, mas cobra um pouco mais de tempo por acesso.

O cache de último nível, normalmente o L3 em CPUs modernas, amplia ainda mais a capacidade e pode ser compartilhado entre vários núcleos, mas também possui latência maior.

Se todos esses níveis falharem, a solicitação precisa chegar à DRAM, que fica muito mais distante em termos de tempo de acesso. O projeto de um cache também envolve muito mais do que capacidade.

A associatividade determina onde cada bloco de dados pode ser armazenado e ajuda a reduzir conflitos, mas buscas mais flexíveis aumentam a complexidade.

A divisão do cache em bancos pode ampliar o número de acessos simultâneos. Múltiplas portas aumentam a taxa de atendimento, mas consomem área e energia.

Caches privados dão aos núcleos acesso local rápido. Caches compartilhados conseguem aproveitar a capacidade total de maneira mais eficiente e simplificam alguns tipos de compartilhamento de dados.

A Intel já abordou esse equilíbrio em pesquisas de arquitetura, tratando a organização do cache como um compromisso entre capacidade, associatividade, latência, largura de banda, estruturas privadas ou compartilhadas, escalabilidade e eficiência energética.

Também existe a coerência de cache. Quando vários núcleos mantêm cópias do mesmo endereço em seus caches, o sistema precisa impedir que eles trabalhem indefinidamente com versões incompatíveis dos dados.

Manter essa visão consistente exige comunicação e controle adicionais, principalmente conforme o número de núcleos cresce. Mais cache, portanto, não traz ganhos automáticos em qualquer situação.

A tecnologia 3D V-Cache da AMD é um bom exemplo de outra abordagem para esse problema. Em vez de aumentar muito a área horizontal do chip da CPU, a empresa empilha verticalmente um die adicional de cache.

Na implementação de segunda geração, a AMD adiciona um die de 64 MB de cache L3 com ligação direta de cobre com cobre e vias que atravessam o silício.

A boa aceitação dos processadores Ryzen X3D em jogos mostra o que acontece quando uma quantidade maior de cache combina com o conjunto de dados usado pela aplicação.

Jogos manipulam continuamente estado do mundo, draw calls, estruturas de física, animações, informações de visibilidade e muitas outras estruturas de dados.

Manter uma parcela maior dessas informações perto da CPU reduz acessos caros à DRAM. Mesmo assim, o ganho varia bastante entre jogos. Alguns já acomodam seus dados críticos em caches menores.

Outros ficam limitados por componentes diferentes. Há também títulos que aproveitam muito bem a capacidade extra. Essa variação é exatamente o que a teoria de hierarquia de memória prevê.

Caches de CPU
Os caches da CPU são organizados em uma hierarquia que equilibra velocidade e capacidade: L1 é o menor e mais rápido, L2 oferece mais capacidade com uma latência um pouco maior, enquanto L3 é normalmente o maior e mais lento nível de cache e geralmente é compartilhado entre vários núcleos da CPU.

O desempenho da DRAM vai muito além de frequência e CAS Latency

Quando um dado não está disponível no cache, ele precisa vir da memória principal, ou DRAM. A DRAM oferece uma capacidade muito maior do que a SRAM por um custo bem menor por bit, mas possui latência superior.

Para compensar isso, sistemas modernos trabalham com largura de banda e paralelismo. A DRAM atual é dividida internamente em bancos e grupos de bancos.

Dentro desses bancos existem linhas formadas por células de memória. O acesso normalmente exige ativar uma linha no buffer de linha antes de ler ou gravar as colunas desejadas.

Isso cria três situações simplificadas. Se a linha necessária já estiver ativa, o controlador consegue um row-buffer hit. Se nenhuma linha útil estiver aberta, a linha correta precisa ser ativada.

Se o banco estiver usando outra linha, ela pode precisar ser fechada antes da ativação da nova. O controlador de memória, portanto, faz muito mais do que encaminhar solicitações da CPU.

Ele acompanha transações pendentes e tenta organizá-las de maneira eficiente entre canais, ranks, grupos de bancos e bancos, respeitando uma grande quantidade de restrições de temporização.

Por isso, largura de banda e latência não devem ser tratadas como a mesma coisa. Latência indica quanto tempo uma operação específica leva. Largura de banda indica quanto dado pode ser transferido durante determinado intervalo.

Um sistema pode oferecer enorme largura de banda total e ainda ter desempenho relativamente ruim em uma sequência de acessos aleatórios dependentes entre si.

Na direção oposta, uma aplicação capaz de manter centenas de transações independentes em andamento pode conviver com uma latência relativamente alta e ainda alcançar uma alta taxa de transferência.

A DDR5 mostra o quanto as memórias atuais dependem de paralelismo. Em comparação com a DDR4, a DDR5 aumenta o número de grupos de bancos, dobra o burst padrão de oito para dezesseis e divide cada DIMM em dois subcanais independentes.

A Micron explica que o aumento no número de grupos de bancos melhora a chance de usar temporizações menos restritivas e possibilita manter mais páginas de memória abertas ao mesmo tempo.

Isso também ajuda a entender por que ajustes de DRAM produzem efeitos tão diferentes entre programas. Aumentar a taxa de transferência amplia a largura de banda teórica. Reduzir timings diminui determinados atrasos.

Acrescentar ranks pode alterar o nível de paralelismo disponível. Depois disso, controlador de memória, arquitetura da CPU e comportamento da aplicação determinam quanto dessa melhoria chega ao software.

Um benchmark de compressão, um jogo, um banco de dados e uma cópia de arquivo grande podem reagir de maneiras muito diferentes à mesma troca de memória.

Memória Crucial DDR5
O desempenho da memória do sistema é determinado por mais do que apenas sua velocidade anunciada e latências primárias.

GDDR e HBM atacam versões diferentes do mesmo problema

GPUs exigem enorme largura de banda porque milhares de unidades de execução podem solicitar dados ao mesmo tempo. Por isso, placas de vídeo voltadas ao consumidor usam memória GDDR ligada por interfaces relativamente largas e trabalhando com taxas de transferência muito altas.

Aceleradores de inteligência artificial e computação de alto desempenho levam esse conceito ainda mais longe com a High Bandwidth Memory, ou HBM.

Em vez de instalar chips de memória ao redor do processador e acessá-los por interfaces convencionais da placa, a HBM empilha dies de DRAM e utiliza conexões extremamente largas posicionadas muito próximas do processador por meio de técnicas avançadas de encapsulamento.

Com isso, é possível alcançar enorme largura de banda e boa eficiência energética por bit transferido, em troca de maior complexidade e custo de fabricação.

A AMD Instinct MI355X mostra a escala atingida atualmente. Um único acelerador possui 288 GB de HBM3E e chega a até 8 TB/s de largura de banda teórica de memória. Sistemas com oito GPUs somam 2,3 TB de HBM.

Esses números seriam excessivos em uma CPU de desktop. Em sistemas modernos de IA, porém, alimentar as unidades de cálculo se tornou uma das principais questões de projeto.

GDDR HBM
As memórias GDDR e HBM enfrentam desafios diferentes no fornecimento de energia para GPUs e aceleradores modernos: a GDDR prioriza alta largura de banda, capacidade e custo-benefício para placas gráficas de consumo, enquanto a HBM prioriza largura de banda extrema e eficiência energética por meio de memória empilhada localizada próxima ao processador, ao custo de maior complexidade e custo de encapsulamento.

A inteligência artificial transformou movimentação de dados em parte central do desempenho

A inteligência artificial generativa moderna, principalmente na forma de Large Language Models, ou LLMs, é um dos melhores exemplos de por que capacidade bruta de cálculo conta apenas uma parte da história.

Grandes redes neurais executam quantidades enormes de operações com matrizes, exatamente o tipo de tarefa para a qual GPUs e aceleradores de IA foram projetados.

Antes do cálculo, porém, o processador precisa buscar pesos do modelo, ativações e dados intermediários. O treinamento consome muita memória porque o sistema precisa guardar muito mais do que os pesos do modelo.

Dependendo da técnica de treinamento e do otimizador, também podem ser necessários gradientes, estados do otimizador, ativações mantidas para backpropagation e áreas temporárias de trabalho.

A inferência elimina parte dessas exigências, mas traz seus próprios problemas. Considere um modelo de linguagem com sete bilhões de parâmetros.

Em precisão de 16 bits, apenas os pesos ocupam cerca de 14 GB, antes de incluir KV cache e a sobrecarga do ambiente de execução.

A quantização ajuda porque reduzir cada parâmetro de 16 bits para 8 ou 4 bits diminui a quantidade de memória necessária para armazenar e transportar o modelo.

Por isso, formatos de baixa precisão trazem benefícios que vão além da economia de capacidade. Se a movimentação de memória for o principal limite, transferir menos bytes pode aumentar diretamente o desempenho.

A ideia também se aplica a outras áreas. Refazer um cálculo e mover dados são custos que podem ser trocados entre si. Em algumas situações, armazenar um valor sai mais barato do que calculá-lo novamente.

Em outras, refazer o cálculo consome menos recursos do que buscar a informação em uma memória distante. Arquiteturas modernas tentam encontrar o melhor ponto dessa troca.

ChatGPT e Claude
Os chatbots modernos baseados em LLM, como o ChatGPT e o Claude, dependem de modelos enormes que constantemente movem pesos, ativações e dados em cache pela memória da GPU, tornando a largura de banda da memória, a capacidade e a localidade dos dados cruciais para uma inferência rápida e eficiente.

Jogos estão entre as cargas de memória mais difíceis de resumir

Não existe uma única carga de trabalho que represente todos os jogos. Em determinado momento, a CPU pode estar percorrendo estruturas de visibilidade e preparando draw calls. Outra thread pode cuidar de física ou inteligência artificial.

Ao mesmo tempo, a GPU processa geometria, lê texturas e materiais, grava render targets, executa shaders e acessa estruturas usadas pela renderização.

Em um jogo de mundo aberto, arquivos também podem ser transferidos do armazenamento para a memória RAM e a VRAM em segundo plano.

Parte dessas operações possui boa coerência de acesso. Outra parte não. Esse comportamento ajuda a explicar várias tendências recentes de hardware.

No lado da CPU, os processadores X3D da AMD usam grandes caches de último nível para manter uma parcela maior dos conjuntos de dados usados pelos jogos.

O benefício pode ser alto quando informações importantes precisariam voltar repetidamente à DRAM em uma CPU com menos cache.

Nas GPUs, caches grandes de último nível também reduzem o tráfego externo com a VRAM, aumentando a largura de banda efetiva e diminuindo bastante a latência no acesso a dados críticos.

NVIDIA, AMD e Intel modificaram bastante as estruturas de cache ao longo das gerações de GPU porque evitar transações fora do chip pode melhorar o desempenho efetivo da memória sem exigir um crescimento equivalente na largura de banda física.

O ray tracing em tempo real complica ainda mais o padrão de acesso. Os raios atravessam Bounding Volume Hierarchies, ou BVHs, estruturas em forma de árvore usadas para determinar quais partes da geometria de uma cena podem ser atingidas.

Diferentemente de cargas tradicionais de rasterização, os raios podem se separar e seguir para regiões completamente diferentes da cena.

A NVIDIA descreve esse comportamento como divergência de execução e de dados: threads vizinhas podem seguir caminhos de código diferentes e acessar endereços difíceis de agrupar ou armazenar em cache de maneira eficiente.

A tecnologia Shader Execution Reordering foi criada, em parte, para reorganizar tarefas de ray tracing e melhorar a localidade de execução e de dados. Esse é um bom exemplo de como memória mais rápida nem sempre é a única resposta.

Em alguns casos, reorganizar o trabalho para aproveitar melhor o subsistema de memória existente traz mais benefício. Jogos também podem ficar limitados pela capacidade, e não pela largura de banda ou latência.

Se texturas, geometria e recursos de renderização ultrapassarem a capacidade da VRAM, o sistema pode precisar mover arquivos pela interface PCI Express a partir da memória do computador ou retirar e carregar novamente determinados recursos.

Uma GPU com enorme largura de banda teórica não consegue compensar dados que nem sequer estão presentes em sua memória local.

O desempenho da memória durante um único frame envolve, portanto, várias dimensões: quanto cabe localmente, quanto tempo leva para acessar cada dado, quão bem os acessos podem ser agrupados, quanto os caches conseguem aproveitar dados reutilizados e quanto tráfego paralelo a arquitetura consegue sustentar.

Ray Tracing
O ray tracing em tempo real em jogos modernos é uma carga de trabalho altamente irregular, com raios potencialmente divergindo para diferentes partes de uma cena e acionando acessos dispersos à geometria, materiais, texturas e dados BVH, o que pode tornar o cache eficiente e o acesso à memória mais difíceis do que em muitas cargas de trabalho de rasterização convencionais. Fonte da imagem: Wikipedia

SSDs mostram por que largura de banda sequencial não explica todo o desempenho

O armazenamento oferece uma das demonstrações mais fáceis de como um único número de largura de banda pode ser enganoso.

SSDs NVMe PCIe modernos anunciam taxas muito altas de leitura e gravação sequencial, mas esse tipo de transferência representa uma situação bastante favorável.

Solicitações grandes e contíguas ajudam o controlador a distribuir o trabalho entre canais, dies e planos de NAND, mantendo várias operações em andamento ao mesmo tempo. Aplicações reais muitas vezes fazem outra coisa.

Sistemas operacionais, jogos, bancos de dados e programas podem solicitar milhares de pequenos blocos espalhados pela unidade. Por isso, as especificações de SSDs separam velocidade sequencial, IOPS aleatório, profundidade de fila e latência.

A documentação da Solidigm sobre cargas de trabalho define acessos sequenciais como blocos adjacentes, acessos aleatórios como blocos espalhados pelo dispositivo e profundidade de fila como a quantidade de solicitações de entrada e saída pendentes.

Aumentar a profundidade de fila pode ampliar a taxa total de transferência ao expor mais trabalho paralelo, mas a latência também cresce.

O conceito é parecido com o funcionamento da DRAM. Um SSD baseado em NAND possui vários dies de memória flash operando em paralelo atrás de um controlador.

A própria NAND é organizada em páginas e blocos de apagamento maiores, e gravações não funcionam como a simples substituição de um byte dentro da DRAM.

O controlador executa tradução de endereços, nivelamento de desgaste, coleta de lixo e outras tarefas em segundo plano para fazer a NAND se comportar como um dispositivo comum de armazenamento em blocos.

Esses processos criam tráfego interno adicional. Um exemplo é a amplificação de gravação, situação na qual o SSD grava fisicamente mais dados do que o computador solicitou.

A coleta de lixo também pode causar picos de latência durante cargas prolongadas. SSDs voltados ao consumidor costumam acrescentar outra camada à hierarquia usando caches rápidos de gravação.

Uma parte da NAND pode trabalhar temporariamente em um modo semelhante ao SLC, recebendo dados rapidamente antes de transferi-los depois para áreas TLC ou QLC mais densas.

Alguns modelos também possuem DRAM para armazenar informações de mapeamento. Projetos sem DRAM podem usar tecnologias como Host Memory Buffer (HMB) para manter parte dessas estruturas dentro da memória RAM do computador.

Mais uma vez, o próprio dispositivo de armazenamento cria sua hierarquia de memória. Quando o cache rápido acaba, a velocidade sustentada de gravação pode ficar bem diferente do pico curto exibido em benchmarks ou páginas de produto.

SSD
Os SSDs PCIe NVMe modernos podem ter velocidades de leitura/gravação sequenciais extremamente rápidas, mas o desempenho no mundo real também depende muito da E/S aleatória, da latência de acesso, da profundidade da fila e da eficiência com que o controlador consegue atender a muitas solicitações pequenas e dispersas. Fonte da imagem: TecFoco

O mesmo hardware pode ser rápido ou lento dependendo da aplicação

É por isso que benchmarks de memória precisam de contexto. Uma cópia de arquivo grande busca largura de banda sequencial sustentada.

Um banco de dados sensível à latência pode depender muito mais de pequenos acessos aleatórios e da latência nos casos mais lentos.

Uma simulação científica trabalhando com arrays densos pode aproveitar muito bem largura de banda contínua e vetorização.

Um compilador pode passar bastante tempo percorrendo estruturas de dados complexas com pouca localidade espacial ou temporal.

Um jogo limitado pela CPU pode ganhar bastante desempenho com um cache de último nível maior caso seu conjunto de dados ativo caiba nele, enquanto outro título pode quase não mudar.

O treinamento de IA exige alta capacidade de cálculo, memória e largura de banda ao mesmo tempo. A decodificação de LLMs com pouca concorrência pode depender muito mais da largura de banda da memória.

Inferências com contextos longos acrescentam o KV cache e transformam capacidade disponível em outro recurso central. Até a palavra largura de banda precisa de contexto.

Existe largura de banda teórica da interface, largura de banda sustentada pela aplicação, largura de banda dos caches, da DRAM, do armazenamento e das interconexões.

Um programa pode saturar uma delas enquanto usa pouco de outra. No fim, o desempenho depende de como o padrão de acesso aos dados da aplicação interage com toda a hierarquia.

Computadores atuais precisam mover menos dados, não apenas movê-los mais rápido

É por isso que algumas das melhorias mais interessantes dos processadores atuais não estão apenas em unidades de cálculo mais rápidas.

Caches mantêm dados reutilizados perto das unidades de execução. Prefetchers tentam prever acessos futuros. Controladores de memória reorganizam solicitações para aproveitar o paralelismo da DRAM.

O agendamento de threads em GPUs tenta melhorar a coerência dos acessos. Técnicas de tiling dividem problemas grandes em conjuntos de dados menores que cabem em memórias rápidas.

Compressão reduz o número de bytes que precisam ser transferidos. Quantização faz algo semelhante em modelos de IA. HBM coloca grandes quantidades de memória fisicamente mais perto dos aceleradores por meio de interfaces muito largas.

Empilhamento 3D aumenta capacidade sem colocar toda a memória no mesmo plano do die. Chiplets dão aos projetistas mais liberdade para combinar estruturas especializadas de processamento, cache e entrada e saída.

Interconexões cada vez mais avançadas tentam impedir que a comunicação entre CPUs, GPUs e aceleradores se transforme no próximo grande gargalo.

A ideia em comum é simples: usar a movimentação cara de dados da forma mais eficiente possível. A capacidade bruta de cálculo continuará crescendo.

A largura de banda da memória também aumentará, caches ficarão maiores, HBM ganhará mais velocidade, SSDs continuarão avançando na faixa de dezenas de gigabytes por segundo e novas técnicas de encapsulamento colocarão componentes antes separados fisicamente cada vez mais próximos.

Mesmo assim, distância física, consumo de energia, capacidade e latência impedem que uma única tecnologia de memória ofereça ao mesmo tempo enorme capacidade, largura de banda muito alta, latência praticamente nula e custo muito baixo.

A hierarquia de memória continuará sendo uma parte central dos computadores e tende a ficar ainda mais profunda e sofisticada.

O desempenho atual depende cada vez mais de colocar o dado certo no nível certo dessa hierarquia antes que o processador precise dele, além de organizar o software para reutilizar informações sempre que possível em vez de transferi-las novamente.

Um núcleo de CPU parado por centenas de ciclos esperando um acesso dependente à memória não está produzindo trabalho útil. Uma GPU com milhares de unidades aritméticas ociosas esperando dados da VRAM também não.

O mesmo vale para um acelerador de IA capaz de trabalhar na escala de petaFLOPS sem receber os dados do modelo no ritmo necessário.

Nem mesmo um SSD de 14 GB/s consegue aproveitar todo o potencial se a aplicação não gerar e processar as solicitações de forma eficiente. Muitas vezes, a operação mais rápida não é buscar o dado em menos tempo. É evitar essa busca.