Zero-copy: ликбез

ANNA Systems
Dec 21, 2018 · 6 min read

Примечание автора: Данная статья ставит целью ознакомить читателя с современными технологиями ускорения передачи данных внутри приложений и ОС. В статье намеренно опускаются многие технические подробности реализации рассматриваемых процессов. Автор рекомендует читателю ознакомиться со стеком графической подсистемы Linux, а также технологией общей памяти и DRMA в первичной документации и в исходных кодах.

Предисловие

С ростом объёма обрабатываемых данных растут и требования к серверному оборудованию. Современные системы обработки Big Data, аналитические DB и кластерные приложения способны обрабатывать десятки гигабайт данных в секунду, иметь десятки или даже сотни терабайт памяти и оперировать десятками петабайт данных. Очевидно, что оптимизации работы подобных систем могут сэкономить десятки, а то и сотни миллионов долларов их владельцам.

Введение в технологии Zero-copy и RDMA

Сегодня мы поговорим о технологиях Zero-copy и RDMA, которые могут существенно ускорить работу почти любого высоконагруженного кластерного приложения или БД, и, в той или мере, используются почти всеми современными системами обработки больших объёмов данных.

Суть технологии Zero-copy заключается в минимизации операций копирования буферов путём использования общей памяти буферов между приложениями, или между приложением и оборудованием, или между ядром и оборудованием. Также вводятся оптимизации и внутри приложения, минимизируя бессмысленные операции копирования буферов одних структур в другие (уменьшается количество буферов, буферы могут переиспользоваться разными структурами).

Классическим примером работы технологии Zero-copy можно назвать передачу данных между приложениями в пределах одного физического компьютера. Чтобы понять разницу между передачей данных с использованием Zero-copy и без.

Допустим, приложению необходимо передать в другое приложение какие-то данные. В классическом случае два приложения должны открыть между собой соединение, инициализировав его через API, и в конце получить два файловых дескриптора: по одному на приложение (не суть важно, какого типа: будь то локальное сетевое соединение, pipe или UNIX-сокет), после чего начинается основная передача данных. В классическом случае протекает она весьма медленно и состоит из следующих этапов:

В итоге для передачи 10Мб данных через буфер 512b может потребоваться более 8000 (!) переключений контекста процессора, не говоря уже о возможной дополнительной обработки данных на уровне ядра, которое тоже требует процессорного времени. Ситуация осложняется тем, что при этих переключениях сбрасывается кэш L1 и управляющие регистры процессора, что ещё больше снижает производительность.

Размер буфера и использование разделяемой памяти (shared memory) при передаче данных

Использование буфера очень большого размера может частично решить ситуацию, однако из-за выделения буфера минимум в трёх местах (ядро, первое и второе приложение) подобная практика может привести к значительному росту потребления оперативной памяти (особенно, если в реальности передавать данные необходимо не только между двумя приложениями). К тому же операции копирования памяти требуют процессорного времени, времени ожидания оперативной памяти (поскольку в L2/L3 кэш такие буферы уже не будут помещаться), что не позволит добиться максимальной оптимизации.

Выходом из этой ситуации является использование разделяемой памяти (shared memory) для буфера передачи данных между приложениями.

В таком случае передача данных между приложениями выглядит следующим образом:

В такой ситуации для передачи 10Мб данных достаточно лишь три раза переключить контекст ядра при использовании общего буфера в 10Мб. При этом (в случае, если кэш процессора достаточно большой) приложения смогут передать данные без ожидания оперативной памяти, т.к. данные осядут в кэше процессора.

Пример из практики

Данный пример может быть недостаточно показательным, поэтому давайте возьмём практическую задачу.

Во многих UNIX-like операционных системах для работы с графикой используется протокол X11. В текущим варианте он имеет множество улучшений, связанных именно с Zero-copy.

Итак, возьмём следующий пример (в упрощенном варианте): необходимо вывести в фреймбуфер видеокарты кадр 4K (3840x2160x24bit) видео из youtube в окне.

Данная операция происходит следующим образом (включая вопросы настройки ядра и соединений):

1. Браузер передаёт серверу Xorg данные буфера изображения (через UNIX-сокет, размера меньше, чем передаваемый буфер).

2. Сервер Xorg передаёт данные композитному менеджеру (через UNIX-сокет, размера меньше, чем передаваемый буфер)

3. Композитный менеджер обрабатывает изображение, а также может производить наложение рамок окон, прозрачность, наложение других окон. После чего сформированный буфер передаётся серверу Xorg (через UNIX-сокет, размера меньше, чем передаваемый буфер)

4. Сервер Xorg передаёт кадр изображения в буфер ядра, где формируется изображение для передачи в фреймбуфер карты (через UNIX-сокет, размера меньше, чем передаваемый буфер)

5. Ядро передаёт в фреймбуфер видеокарты данные буфера из оперативной памяти (через вызов функции видеокарты копирования данных из оперативной памяти в теневой буфер видеокарты)

6. Ядро асинхронно ожидает завершения копирования данных в теневой буфер видеокарты (через получение прерывания от видеокарты).

7. Ядро отсылает видеокарте команду смены основного и теневого буферов.

8. Видеокарта обрисовывает кадр на экран.

Как видно из схемы, данные пересылаются множество раз через UNIX сокеты между несколькими приложениями, при этом постоянно переключая контекст ядра. Учитывая, что UNIX-сокеты обычно оперируют максимальным значением 64Kb на буфер, для передачи одного кадра видео 4К (около 23.7Мб) необходимо совершить минимум 1140 переключений контекста процессора. Если произвести расчёт количества переключений с пункта 1 по 4, то их будет как минимум 4560. Также композитный менеджер тратит много тактов процессора на выполнение графических операций с окнами (наложение, обрисовка рамок и др. функционал)

Оптимизация в современном графическом стеке Linux систем

Для уменьшения нагрузки в современных системах был проделан ряд оптимизаций:

Заключение

Таким образом получается сократить количество переключений контекста с более чем тысячи до десятков, а также уменьшить время обработки изображения с использованием аппаратного ускорения GPU. Также уменьшается нагрузка на процессор и оперативную память.

Подобным образом (общая память между приложениями, между приложениями и устройствами, а также между приложениями на разных хостах) ускоряются многие операции. В следующей статье мы разберем технологию RDMA, а также посмотрим, как задержки сети, переключения контекста процессора, а также сам сетевой стек могут уменьшать производительность в сотни раз.

В компании Anna Systems мы пытаемся максимально использовать современные подходы к вычислениям, поэтому в системах симуляции гидродинамики, электромагнитных полей, нейронных сетей и другого высокопроизводительного и требовательного ПО активно применяются различные технологии снижения задержек: от грамотной оркестрации (предотвращающей вымывания кэшей) и zero-copy до MPI-over-RDMA. Применение данного стека технологий позволяет более эффективно использовать вычислительные машины, и, как следствие, предоставлять нашим клиентам результаты расчётов быстрее и за меньшие деньги. Мы стремимся к максимальной интеграции бизнес-требований с конечным IT-решением.

Автор:

Симакин Владимир Андреевич,

ИТ-Архитектор ANNA Systems

ANNA Systems

Hyperscale Blockchain GRID protocol of the fractal infrastructure

ANNA Systems

Written by

#Hyperscale Blockchain GRID protocol of the fractal infrastructure. Revolutionary changing #HPC and #Blockchain markets

ANNA Systems

Hyperscale Blockchain GRID protocol of the fractal infrastructure