Затримка пам'яті на хмарному сервері: виміряно, а не вгадано

Затримка пам'яті на хмарному сервері: виміряно, а не вгадано

Затримка пам'яті на хмарному сервері: виміряно, а не вгадано

Коротко (перевірено 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. Виправлені версії, скрипт перевірки, перезбірка.