Профилировать NUMA влияние на latency

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Профилировать NUMA влияние на latency

1. Цель задачи

Научиться измерять и анализировать влияние NUMA-топологии на латентность доступа к оперативной памяти в многосокетных системах (AMD EPYC). Выполнить серию экспериментов с привязкой процессов к разным NUMA-узлам с помощью numactl, зафиксировать разницу в latency между локальным и удалённым доступом к памяти. Закрепить понимание того, как архитектура NUMA влияет на производительность latency-чувствительных приложений.

Ключевой результат Документ с отчётом, содержащим результаты замеров latency на локальной и удалённой NUMA-ноде, подтверждающий разницу в 2–3 раза (или более), а также практические рекомендации на основе полученных данных.


2. Исходные данные

Что нужноОткуда взять
Сервер с NUMA-архитектурой (например, AMD EPYC, 2+ NUMA-узла)Физическая машина / облачный bare-metal инстанс
ОС Linux (ядро 5.x+) с поддержкой NUMAУстановленная система, `dmesg
Утилита numactlРепозиторий дистрибутива (apt / yum)
Утилита perflinux-tools-common / linux-tools-generic
Микро-бенчмарк для измерения латентности памятиСобственная программа на C / lmbench / mbw + stress-ng
Графический инструмент (gnuplot / matplotlib)Для построения графиков, необязательно

Если нет реального инструмента — симулируем:

  1. Использовать QEMU/KVM с эмуляцией нескольких NUMA-узлов:
    qemu-system-x86_64 -m 8G -smp 4 -numa node,cpus=0-1,memdev=mem1 -numa node,cpus=2-3,memdev=mem2 -object memory-backend-ram,id=mem1,size=4G -object memory-backend-ram,id=mem2,size=4G
    
  2. В облачных средах (AWS / GCP) выбрать bare-metal инстансы с multi-socket (например, m5n.metal).
  3. Если доступен только односокетный процессор — запустить numactl --hardware и убедиться, что есть как минимум два узла (возможно, через эмуляцию подсистемы; в противном случае задание невыполнимо – нужно дополнить наблюдением: «на односокетных системах разница будет минимальна, эксперимент не имеет смысла»).

3. Технологический стек

КомпонентИнструментыНазначение
Операционная системаLinux (Ubuntu 22.04 / RHEL 9)Запуск тестов
Управление NUMAnumactl, numastatПривязка потоков/памяти, просмотр топологии
Профилированиеperf, likwid-perfctrЗамеры циклов, латентности кэша/памяти
Микро-бенчмаркlmbench (lat_mem_rd), stress-ng, mbwИзмерение latency и bandwidth
ПрограммированиеC / Python (ctypes)Собственный бенчмарк (опционально)
Визуализацияgnuplot / matplotlibГрафик latency против размера рабочего набора
АнализBash, awk, скриптыОбработка сырых данных

4. Этапы выполнения

Этап 1: Изучение NUMA-топологии и подготовка окружения (20–30 мин)

Действия

  1. Выполните команду numactl --hardware и запишите:
    • количество NUMA-узлов;
    • какие CPU и область памяти привязаны к каждому узлу;
    • расстояние между узлами (distance).
    available: 2 nodes (0-1)
    node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
    node 0 size: 128929 MB
    node 0 free: 118629 MB
    node 1 cpus: 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
    node 1 size: 129021 MB
    node 1 free: 119028 MB
    node distances:
    node   0   1
      0:  10  21
      1:  21  10
    
  2. Убедитесь, что numactl, numastat, perf, lmbench (или его аналоги) установлены. Если нет — установите:
    sudo apt install numactl linux-tools-common lmbench stress-ng mbw
    
  3. Соберите lat_mem_rd из пакета lmbench (или используйте готовый бинарник).

Ожидаемый результат этапа Полная карта топологии NUMA, готовый инструментарий.


Этап 2: Измерение локальной латентности (30–40 мин)

Действия

  1. Запустите lat_mem_rd на процессоре и памяти одного и того же NUMA-узла (например, узел 0):

    numactl --cpunodebind=0 --membind=0 lat_mem_rd -W 100 -N 100 -t 128M 2>&1 | tee local_0.log
    

    (где -W — количество разогрева, -N — количество измерений, -t — размер доступа, 128M — размер рабочего набора, превышающий L3-кэш).

  2. Выполните тест для разных размеров рабочего набора (stride) — от 1K до 128M (можно скриптом):

    for s in 1K 8K 64K 512K 4M 32M 128M; do
      numactl --cpunodebind=0 --membind=0 lat_mem_rd -W 100 -N 100 -t $s 2>&1 | tee -a local_results.txt
    done
    
  3. Извлеките из вывода среднее значение latency (в наносекундах) для последнего, наибольшего размера (обычно он показывает latency DRAM).

Ожидаемый результат этапа Файл local_results.txt с таблицей «размер – latency» для локального доступа.


Этап 3: Измерение удалённой латентности (30–40 мин)

Действия

  1. Запустите тот же бенчмарк с привязкой процессора к узлу 0, но памяти — к узлу 1 (remote access):
    numactl --cpunodebind=0 --membind=1 lat_mem_rd -W 100 -N 100 -t 128M 2>&1 | tee remote_01.log
    
  2. Повторите сканирование по размерам, сохраняя в remote_results.txt.
  3. Выполните обратный случай: процесс на узле 1, память на узле 0.
  4. Для каждого измерения запишите также значение из /sys/devices/system/cpu/cpu*/cache/index*/ для информации о кэшах (необязательно).

Ожидаемый результат этапа Файл remote_results.txt с аналогичной структурой.


Этап 4: Дополнительные эксперименты (1–1.5 ч)

Действия

  1. Влияние количества потоков – запуск multithreaded-бенчмарка (stress-ng) с разным числом потоков:
    numactl --cpunodebind=0 --membind=0 stress-ng --memrate 0 --mem-ops 100000 --mem-rate-method cache-miss --numa-rast --taskset 0-3
    
    Замерьте среднюю латентность через perf stat -e cache-misses,LLC-load-misses.
  2. Bandwidth test – с помощью mbw:
    numactl --cpunodebind=0 --membind=0 mbw -n 10 -t memcpy -b 4096
    numactl --cpunodebind=0 --membind=1 mbw -n 10 -t memcpy -b 4096
    
    Сравните пропускную способность.
  3. Гибридная привязка (interleaving) – запуск numactl --interleave=all – замерить среднюю латентность.

Ожидаемый результат этапа Дополнительные данные, подтверждающие влияние NUMA на разные виды нагрузок.


Этап 5: Анализ и отчёт (30–60 мин)

Действия

  1. Соберите все данные из логов в таблицу (например, в CSV):

    Размер (bytes)Local latency (ns)Remote latency (ns)Ratio
    ............
  2. Постройте график (используя matplotlib или gnuplot): latency vs размер, две кривые (local, remote). Убедитесь, что на размере > 64M (DRAM) разница ≥ 2x.

  3. Оцените impact на реальные приложения: если приложение обращается к большому объему данных и неправильно привязано к NUMA, latency может ухудшиться в разы. Напишите краткие рекомендации:

    • для latency-чувствительных сервисов (базы данных, real-time) привязывать процессы к одному NUMA-узлу с собственной памятью;
    • использовать numactl --membind для выделения памяти рядом с потоком;
    • избегать случайного распределения при большом объёме данных.
  4. Оформление отчёта: report_numa_latency.md.

Ожидаемый результат этапа Готовый отчёт с таблицами, графиком, выводами и рекомендациями.


5. Критерии приемки (Definition of Done)

  • Выполнен сбор информации о NUMA-топологии (numactl --hardware).
  • Замерена латентность на локальном доступе для набора размеров от 1K до 128M.
  • Замерена латентность на удалённом доступе (cross-node) для тех же размеров.
  • Коэффициент удалённой задержки (remote / local) при размере > 64M не менее 2.0.
  • Построен график зависимости latency от размера рабочего набора с двумя кривыми.
  • Проведён хотя бы один дополнительный тест (multithreaded latency, bandwidth).
  • Подготовлен markdown-отчёт с таблицей, графиком и рекомендациями.
  • Все команды привязки выполняются через numactl (не через taskset без memory binding).
  • Исключены известные артефакты (освобождение кэша, разогрев, достаточное количество итераций).

6. Ожидаемый результат

ФайлСодержание
report_numa_latency.mdОтчёт: топология, методология, таблица замеров, график (base64 embedded PNG или ссылка), выводы, рекомендации
local_results.txtСырые данные локальных замеров
remote_results.txtСырые данные удалённых замеров
(опционально) scatter.png / latency_plot.pngГрафик в png
(опционально) additional_experiments.logЛоги запуска stress-ng, mbw

7. Возможные сложности и их решение

СложностьРешение
В системе всего один NUMA-узел (онли node 0)Задание невыполнимо; в отчёте указать «single-socket, NUMA отсутствует, тест не имеет смысла». Можно использовать QEMU VM с эмуляцией.
отсутствует lmbench (или lat_mem_rd)Использовать stress-ng --memrate + perf или написать простой враппер на C (см. листинг ниже).
Высокие фоновые нагрузкиЗапускать тесты на изолированных ядрах (isolcpus в ядре) или с nice -n -20.
Первые замеры показывают аномально высокую латностьУбедиться, что кэш очищен: выполнить drop_caches перед каждым замером (echo 3 > /proc/sys/vm/drop_caches).
Показания lat_mem_rd колеблютсяУвеличить количество повторений (-N 200), проверить отсутствие троттлинга по питанию.

Пример простого C-бенчмарка для замера латентности чтения (для справки):

#include <numa.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define SIZE (128 * 1024 * 1024) // 128 MB
#define STRIDE 64                 // bytes
#define ITERATIONS 100

int main() {
    char *buf = (char *)numa_alloc_local(SIZE);
    for (int i = 0; i < SIZE; i += 64) buf[i] = 0;
    struct timespec t1, t2;
    clock_gettime(CLOCK_MONOTONIC, &t1);
    for (int k = 0; k < ITERATIONS; k++) {
        volatile char tmp;
        for (int i = 0; i < SIZE; i += STRIDE) {
            tmp = buf[i];
        }
    }
    clock_gettime(CLOCK_MONOTONIC, &t2);
    double ns = (t2.tv_sec - t1.tv_sec) * 1e9 + (t2.tv_nsec - t1.tv_nsec);
    printf("Latency: %.2f ns per access\n", ns / (ITERATIONS * (SIZE/STRIDE)));
    numa_free(buf, SIZE);
    return 0;
}

8. Бюджет времени (оценка)

ЭтапВремя
Этап 1. Изучение топологии и установка инструментов20–30 мин
Этап 2. Локальные замеры latency30–40 мин
Этап 3. Удалённые замеры latency30–40 мин
Этап 4. Дополнительные эксперименты1–1.5 ч
Этап 5. Анализ и отчёт30–60 мин
Итого3–4 ч

Примечание: при первом знакомстве с NUMA и инструментами может потребоваться 5–6 часов.


9. Связанные вопросы из базы знаний

ВопросТема
45NUMA архитектура и топология
67Латентность оперативной памяти и кэша
89Профилирование доступа к памяти (perf, likwid)
112Использование numactl для управления привязкой
234Особенности AMD EPYC (CCX, multiple dies)
345Латентность vs пропускная способность (bandwidth)
456Методы микробенчмаркинга (reproducibility)
567Анализ LLC misses и удалённых обращений
678Влияние кэш-памяти на производительность
789TMA (Top-down Microarchitecture Analysis)
890Измерение времени с помощью RDTSC

10. Чек-лист самопроверки

  • Я проверил(а) NUMA-топологию командами numactl --hardware и lscpu | grep -i numa.
  • Я убедился(ась), что замеры локального доступа выполняются с привязкой и процесса, и памяти к одному узлу.
  • Я зафиксировал(а) размер рабочего набора, превышающий кэш последнего уровня (L3) для достижения DRAM latency.
  • Я выполнил(а) как минимум два полных запуска на разных узлах (node0→node1 и node1→node0).
  • Я построил(а) график и проверил(а), что значения remote/local ratio для последнего размера ≥ 2.0.
  • Я написал(а) отчёт с интерпретацией результатов и практическими рекомендациями для настройки приложений в NUMA-среде.