Задержка памяти на облачном сервере: измерено, а не угадано

Задержка памяти на облачном сервере: измерено, а не угадано

Задержка памяти на облачном сервере: измерено, а не угадано

Коротко (проверено 2026-11-01): Пост, попавший на главную Hacker News в эти выходные, утверждает: процессор, показывающий 100% загрузки, часто просто ждёт RAM. Автор показывает GROUP BY, который ускорился, выполняя больше работы, но реже обращаясь к памяти. Мы проверили ту же идею на облачном сервере, который пишет этот блог: одно чтение стоит около 2 нс, когда данные помещаются в L1, и около 200 нс при рабочем наборе 256 МБ, примерно в сто раз больше. Для тех, кто арендует серверы, этот разрыв важнее числа ядер в заказе. Ниже скрипт, наши цифры и что они меняют в выборе между облаком и выделенным сервером.

Источник. "It's the memory latency, stupid", автор Maor Kern (Pivot / Epsio Labs, опубликовано 2026-10-28), обсуждение на Hacker News, 175 баллов на 2026-11-01. Их цифры приведены так, как их печатает пост; наши мы измерили сами.

Что измерил пост Pivot

Тест - один запрос, GROUP BY UserID по таблице ClickBench "hits" (около 99 миллионов строк, около 17 миллионов уникальных пользователей), закреплённый на одном ядре bare-metal сервера AMD EPYC 9124 со 128 ГБ DDR5. Обычная хеш-таблица на 17 миллионов ключей не помещается ни в один кеш, поэтому почти каждый поиск уходит в RAM. Если сначала разбить строки на 4096 разделов, каждый со своей маленькой хеш-таблицей, работы становится больше, но каждая таблица остаётся в кеше:

Показатель (одно ядро) Обычная хеш-таблица Radix-разбиение
Время, 1-й / 2-й запуск 3.9 с / 3.6 с 3.3 с / 2.7 с
Слоты, ждущие память 50.1% 23.5%
Слоты с полезной работой 16.1% 32.2%
Чтения из DRAM 76.2 млн 25.7 млн

Версия с разбиением выполняет больше операций (19.4 млрд против 11.4 млрд) и всё равно финиширует первой. Оптимизированная версия автора отработала за 1.85 с на одном ядре и за 0.17 с на всех 16. Урок не про одну базу данных: "CPU 100%" в top значит, что ядро не простаивает, а не что оно считает.

Измерьте сами: memlat.js

Классический способ увидеть задержку - pointer chase: массив, где каждая ячейка хранит индекс следующей, в случайном порядке, на расстоянии одной кеш-линии (64 байта). Каждое чтение зависит от предыдущего, поэтому процессор не может ни загрузить данные заранее, ни выполнить чтения параллельно, и время одного шага равно задержке того уровня, где живёт рабочий набор. На нашей машине для блога нет компилятора C, поэтому это JavaScript для Bun: JIT превращает цикл в несколько машинных инструкций, что немного добавляет к каждой цифре. Сравнивайте ступени, а не абсолютные значения. Сохраните как 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

Результат, запущено на облачном сервере DCXV с Bun 1.4.4 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

Второй запуск тем же утром отличался не больше чем на 2 нс до 1 МБ и не больше чем на 12 нс выше:

Рабочий набор нс на чтение, запуск 1 / 2 Где живёт
32 КБ 2.2 / 2.4 кеш данных L1
256 КБ 12.5 / 10.5 L2, частично уже L3
1 МБ 24.3 / 24.3 L3
16 МБ 138.4 / 137.2 обход таблиц страниц, L3 и DRAM
256 МБ 205.1 / 193.5 DRAM

Что говорят цифры

  1. Гостевая система не видит настоящих кешей. ВМ сообщает L2 на 4 МБ и L3 на 16 МБ; Intel для Xeon E5-2698 v4 указывает 50 МБ кеша (Intel ark, проверено 2026-11-01). Гипервизор показывает свою топологию, и lscpu в облачном сервере читает её, а не кремний. Измеряйте железо, а не доверяйте подписям.
  2. Обрыв наступает задолго до заполнения кеша. Между 1 МБ и 4 МБ задержка растёт в пять раз, хотя до 50 МБ далеко. Со страницами 4 КБ случайный обход 4 МБ затрагивает 1 024 страницы, больше, чем вмещает TLB, а в ВМ промах TLB обходит и гостевые, и хостовые таблицы страниц; к тому же L3 общий с другими ядрами сокета. Эти причины мы не разделяли. Практический вывод тот же: в ВМ планируйте так, будто ваш быстрый рабочий набор - несколько мегабайт.
  3. Больше RAM не значит быстрее RAM. От 1 МБ до 256 МБ цена одного чтения растёт примерно в восемь раз. Дополнительные гигабайты дают объём, а не скорость; задачу, упирающуюся в задержку, ускоряет меньший горячий набор, как показывает разбиение в Pivot.

Что это значит для аренды серверов

  • Облачные серверы подходят большинству веб-приложений, API и небольших баз: их горячие данные помещаются в кеш, и они ждут сеть или диск задолго до того, как начнут ждать RAM. Выбирайте размер по измеренному потреблению памяти, как в нашем руководстве по выбору размера облачного сервера.
  • Выделенные серверы окупаются, когда большой рабочий набор весь день читается случайно: аналитика, большие хеш-соединения, кеши в памяти, сервер PostgreSQL, индексы которого уже не помещаются в RAM. Вы получаете весь L3 и все каналы памяти без соседей и можете включить huge pages, которые уменьшают промахи TLB. Соберите конфигурацию на странице выделенного сервера.
  • Перед любым апгрейдом запустите тест на сервере, который уже есть, и проверьте, упирается ли медленная задача в задержку. Удвоение ядер ничего не даст одному потоку, который половину времени стоит. Сама память в этом году подорожала, см. серверная RAM в 2026, так что самое дешёвое решение часто - код, который трогает её меньше.

Проверьте до покупки

Наши дата-центры Tier III в Праге и Ковильяне запускают облачные серверы и выделенные серверы. Запустите облачный сервер, выполните memlat.js рядом со своей нагрузкой и пришлите результат на sales@dcxv.com: мы поможем решить, изменит ли цифры выделенный сервер.

10 000 выступлений в советах США: что важно клиентам ЦОД
clouddedicatedinfrastructure

10 000 выступлений в советах США: что важно клиентам ЦОД

Новый датасет кодирует 10 202 выступления в советах США о дата-центрах: 90,5% против, вода на первом месте. Мы пересчитали данные и объясняем, что это меняет.

Задержка памяти на облачном сервере: измерено, а не угадано
clouddedicatedinfrastructure

Задержка памяти на облачном сервере: измерено, а не угадано

Популярный пост утверждает, что занятый CPU часто просто ждёт RAM. Мы измерили задержку от L1 до DRAM на нашем облачном сервере и объясняем, что это меняет.

Альтман хочет вашу DRAM в дата-центрах: серверная RAM в 2026
cloudaipricinginfrastructure

Альтман хочет вашу DRAM в дата-центрах: серверная RAM в 2026

Сэм Альтман считает, что память в ПК простаивает и должна быть в дата-центрах. Что дефицит DRAM 2026 значит для аренды серверов, и скрипт для замера RAM.

PHP2Shell: проверка и патч RCE-уязвимостей PHP на сервере
clouddedicatedhosting

PHP2Shell: проверка и патч RCE-уязвимостей PHP на сервере

PHP2Shell (CVE-2026-109618, CVE-2026-109619) позволяет запросу без авторизации выполнить код в PHP. Исправленные версии, проверенный скрипт, меры в php.ini и план обновления.

Brotli CVE-2026-111497: обновление Node.js, Bun и Python
cloudnetworkinginfrastructure

Brotli CVE-2026-111497: обновление Node.js, Bun и Python

Уязвимость декодера Brotli (CVE-2026-111497) позволяет выполнить код через fetch() в Node.js и Bun или HTTP-клиенты Python. Исправленные версии, скрипт проверки, пересборка.