Latência de memória num servidor cloud: medida, não suposta

Latência de memória num servidor cloud: medida, não suposta

Latência de memória num servidor cloud: medida, não suposta

Resposta curta (verificado em 2026-11-01): Um post que chegou à primeira página do Hacker News neste fim de semana defende que um CPU a 100% muitas vezes está apenas à espera da RAM, e mostra um GROUP BY que ficou mais rápido fazendo mais trabalho mas tocando menos na memória. Testámos a mesma ideia no servidor cloud que escreve este blog: uma leitura custa cerca de 2 ns quando os dados cabem na L1 e cerca de 200 ns com um conjunto de trabalho de 256 MB, umas cem vezes mais. Para quem aluga servidores, essa diferença conta mais do que o número de núcleos na encomenda. Abaixo estão o script, os nossos números e o que mudam na escolha entre cloud e dedicado.

Fonte. "It's the memory latency, stupid", de Maor Kern (Pivot / Epsio Labs, publicado em 2026-10-28), discutido no Hacker News com 175 pontos até 2026-11-01. Os números deles vêm como o post os publica; os nossos foram medidos por nós.

O que o post da Pivot mediu

O teste é uma consulta, GROUP BY UserID sobre a tabela "hits" do ClickBench (cerca de 99 milhões de linhas, cerca de 17 milhões de utilizadores distintos), fixada num núcleo de um servidor bare metal AMD EPYC 9124 com 128 GB DDR5. Uma tabela hash simples com 17 milhões de chaves não cabe em nenhuma cache, por isso quase cada pesquisa vai à RAM. Dividir primeiro as linhas em 4096 partições, cada uma com uma tabela hash pequena, acrescenta trabalho mas mantém cada tabela em cache:

Métrica (um núcleo) Tabela hash simples Particionamento radix
Tempo, 1.ª / 2.ª execução 3.9 s / 3.6 s 3.3 s / 2.7 s
Slots à espera de memória 50.1% 23.5%
Slots com trabalho útil 16.1% 32.2%
Leituras da DRAM 76.2 milhões 25.7 milhões

A versão particionada executa mais operações (19.4 mil milhões contra 11.4 mil milhões) e mesmo assim termina primeiro. A versão otimizada do autor correu em 1.85 s num núcleo e em 0.17 s nos 16. A lição não é sobre uma só base de dados: "CPU 100%" no top diz que o núcleo não está parado, não que está a calcular.

Meça você mesmo: memlat.js

A forma clássica de ver a latência é um pointer chase: um array em que cada posição guarda o índice da seguinte, em ordem aleatória, separadas por uma linha de cache (64 bytes). Cada leitura depende da anterior, por isso o CPU não consegue pré-carregar nem sobrepô-las, e o tempo por salto é a latência do nível onde vive o conjunto de trabalho. Na nossa máquina do blog não há compilador de C, por isso é JavaScript para Bun: o JIT transforma o ciclo em poucas instruções de máquina, o que acrescenta um pouco a cada número. Compare os degraus, não os valores absolutos. Guarde-o como memlat.js:

// memlat.js - pointer-chase memory latency by working-set size (bun memlat.js)
import { readFileSync, readdirSync } from 'node:fs'

const cpu = readFileSync('/proc/cpuinfo', 'utf8').match(/model name\s*:\s*(.+)/)?.[1] ?? '?'
const base = '/sys/devices/system/cpu/cpu0/cache'
const caches = readdirSync(base).filter(d => d.startsWith('index')).map(d => {
  const r = f => readFileSync(`${base}/${d}/${f}`, 'utf8').trim()
  return `L${r('level')}${r('type') === 'Data' ? 'd' : r('type') === 'Instruction' ? 'i' : ''} ${r('size')}`
})
console.log(`CPU: ${cpu}\nCaches: ${caches.join(', ')}`)

// one random cycle through n slots, 64 bytes (16 x int32) apart: one cache line per hop
function ring(bytes) {
  const n = Math.max(2, Math.floor(bytes / 64))
  const order = new Uint32Array(n)
  for (let i = 0; i < n; i++) order[i] = i
  for (let i = n - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1))
    const t = order[i]; order[i] = order[j]; order[j] = t
  }
  const a = new Int32Array(n * 16)
  for (let i = 0; i < n; i++) a[order[i] * 16] = order[(i + 1) % n] * 16
  return a
}

function chase(a, hops) {
  let p = 0
  for (let i = 0; i < hops; i++) p = a[p]
  return p
}

const hops = 20_000_000
console.log('working set   ns/load')
for (const kb of [16, 32, 256, 1024, 4096, 16384, 65536, 262144]) {
  const a = ring(kb * 1024)
  chase(a, hops / 4) // warm up: JIT and caches
  const best = []
  for (let r = 0; r < 3; r++) {
    const t = Bun.nanoseconds()
    const sink = chase(a, hops)
    best.push((Bun.nanoseconds() - t) / hops)
    if (sink < 0) console.log(sink)
  }
  const label = kb >= 1024 ? `${kb / 1024} MB` : `${kb} KB`
  console.log(`${label.padStart(10)}   ${Math.min(...best).toFixed(1).padStart(7)}`)
}
bun memlat.js

Resultado, executado num servidor DCXV Cloud com Bun 1.4.4 em 2026-11-01:

CPU: Intel(R) Xeon(R) CPU E5-2698 v4 @ 2.20GHz
Caches: L2 4096K, L1d 32K, L3 16384K, L1i 32K
working set   ns/load
     16 KB       2.0
     32 KB       2.2
    256 KB      12.5
      1 MB      24.3
      4 MB     122.2
     16 MB     138.4
     64 MB     164.7
    256 MB     205.1

Uma segunda execução na mesma manhã variou no máximo 2 ns até 1 MB e no máximo 12 ns acima disso:

Conjunto de trabalho ns por leitura, execução 1 / 2 Onde vive
32 KB 2.2 / 2.4 cache de dados L1
256 KB 12.5 / 10.5 L2, a transbordar para a L3
1 MB 24.3 / 24.3 L3
16 MB 138.4 / 137.2 travessias de tabelas de páginas, L3 e DRAM
256 MB 205.1 / 193.5 DRAM

O que os números dizem

  1. O convidado não vê as caches reais. A VM declara 4 MB de L2 e 16 MB de L3; a Intel indica 50 MB de cache para o Xeon E5-2698 v4 (Intel ark, verificado em 2026-11-01). O hipervisor apresenta a sua própria topologia, e o lscpu dentro de um servidor cloud lê essa, não o silício. Meça o hardware em vez de confiar nas etiquetas.
  2. A queda chega muito antes de a cache encher. Entre 1 MB e 4 MB a latência multiplica-se por cinco, muito abaixo dos 50 MB. Com páginas de 4 KB, um percurso aleatório de 4 MB toca 1.024 páginas, mais do que a TLB guarda, e numa VM uma falha de TLB percorre as tabelas de páginas do convidado e do anfitrião; além disso, a L3 é partilhada com os outros núcleos do socket. Não separámos estas causas. A conclusão prática é a mesma: numa VM, planeie como se o seu conjunto de trabalho rápido tivesse poucos megabytes.
  3. Mais RAM não é RAM mais rápida. De 1 MB a 256 MB o custo por leitura cresce cerca de oito vezes. Mais gigabytes dão capacidade, não velocidade; o que acelera uma tarefa limitada pela latência é um conjunto quente menor, como mostra o particionamento da Pivot.

O que significa se aluga servidores

  • Os servidores cloud servem a maioria das apps web, APIs e bases de dados pequenas: os seus dados quentes cabem em cache, e esperam pela rede ou pelo disco muito antes de esperarem pela RAM. Dimensione-os pelo uso de memória medido, como no nosso guia de dimensionamento de servidores cloud.
  • Os servidores dedicados compensam quando um conjunto de trabalho grande é lido ao acaso o dia todo: análise de dados, hash joins grandes, caches em memória, um servidor PostgreSQL cujos índices já não cabem na RAM. Fica com toda a L3 e todos os canais de memória sem vizinhos, e pode ativar huge pages, que reduzem as falhas de TLB. Configure um na página de servidor dedicado.
  • Antes de fazer upgrade a qualquer um, corra o teste no servidor que já tem e veja se a sua tarefa lenta está limitada pela latência. Duplicar os núcleos não ajuda uma thread que passa metade do tempo parada. A própria memória ficou mais cara este ano, ver RAM de servidor em 2026, por isso a solução mais barata é muitas vezes código que lhe toca menos.

Teste antes de comprar

Os nossos data centers Tier III em Praga e na Covilhã disponibilizam servidores cloud e servidores dedicados. Inicie um servidor cloud, corra memlat.js ao lado da sua própria carga e envie o resultado para sales@dcxv.com: ajudamos a decidir se um servidor dedicado mudaria os números.

10.000 comentários em câmaras dos EUA: o que significam
clouddedicatedinfrastructure

10.000 comentários em câmaras dos EUA: o que significam

Um conjunto de dados codifica 10.202 intervenções sobre data centers nos EUA: 90,5% contra, a água à frente. Recontámos e explicamos o que muda para clientes.

Latência de memória num servidor cloud: medida, não suposta
clouddedicatedinfrastructure

Latência de memória num servidor cloud: medida, não suposta

Um post viral diz que um CPU ocupado muitas vezes só espera pela RAM. Medimos a latência de L1 até DRAM no nosso servidor cloud e explicamos o que isso muda.

Altman quer a sua DRAM no data center: RAM de servidor 2026
cloudaipricinginfrastructure

Altman quer a sua DRAM no data center: RAM de servidor 2026

Sam Altman diz que a memória dos PC fica ociosa e deve ir para o data center. O que a escassez de DRAM de 2026 muda para servidores e um script para a medir.

PHP2Shell: verificar e corrigir as falhas RCE do PHP
clouddedicatedhosting

PHP2Shell: verificar e corrigir as falhas RCE do PHP

PHP2Shell (CVE-2026-109618, CVE-2026-109619) deixa um pedido sem autenticação executar código no PHP. Versões corrigidas, script testado, ajustes no php.ini e plano.

Brotli CVE-2026-111497: corrija apps Node.js, Bun e Python
cloudnetworkinginfrastructure

Brotli CVE-2026-111497: corrija apps Node.js, Bun e Python

Uma falha no decodificador Brotli (CVE-2026-111497) executa código via fetch() no Node.js e Bun ou clientes HTTP Python. Versões corrigidas, script testado e rebuild.