Максим Хижинский, C++ CoreHard Autumn 2017
ПрологПролог
3 вечных вопроса
Что делать?Что делать?
Кто виноват?Кто виноват?
Максим Хижинский, C++ CoreHard Autumn 2017
ПрологПролог
3 вечных вопроса
Что делать?Что делать?
Кто виноват?Кто виноват? Почему такПочему так
получилось?получилось?
Как сделать?Как сделать?
Максим Хижинский, C++ CoreHard Autumn 2017
4x10G NIC (Network Interface Card)4x10G NIC (Network Interface Card)
Максим Хижинский, C++ CoreHard Autumn 2017
4x10G NIC
Инструменты:
●
Netmap — opensource
●
pf_ring — proprietary
●
DPDK (Data Plane Dev Kit) — opensource
Key features:
●
Работа в userspace, минуя kernel
●
DMA, no copy
Максим Хижинский, C++ CoreHard Autumn 2017
4x10G NIC4x10G NIC
Пакет = 512 байт = 2**9 байт = 2**17 бит
Поток = 40 x 2**30 бит
Итого: 40 x 2**30 / 2**17 = 40 x 2**13 пакетов/сек
Процессор = 4 ГГц ~ 4 x 2**30
На пакет: 4 x 2**30 / 40 x 2**13 = 0.1 x 2**17
~ 2**14 = 16K тактов на пакет
Максим Хижинский, C++ CoreHard Autumn 2017
Port 0
RSS RSS RSS RSS
Port 3
RSS RSS RSS RSS
Port 1
RSS RSS RSS RSS
Port 2
RSS RSS RSS RSS
NIC Application
Thread 0
Thread 1
Thread 2
Thread 3
Thread 4
Thread 5
Thread 6
Thread 7
Thread N
Hash
Порочные практикиПорочные практики
Безумное количество потоков — потоков
много больше, чем ядер
Создание потоков «на лету», под
выполнение конкретного действия
Максим Хижинский, C++ CoreHard Autumn 2017
Распараллеливание
Thread Thread Thread Thread Thread Thread
Число потоков <= число ядер
Распараллеливание
Thread Thread Thread Thread Thread
Core 0 — свободен (для OS)
Hard pinning threads to cores
Worker thread
ХарактеристикиХарактеристики:
●
No memory allocation
●
Hard pinned to processor
core
●
No memcpy (по
возможности)
Thread
In port
Out port
Максим Хижинский, C++ CoreHard Autumn 2017
Worker thread
Характеристики:
●
No memory allocation (= no std containers)
●
Hard pinned to processor core
●
No memcpy (по возможности)
ПроблемыПроблемы:
●
Длительные/отложенные действия
(авторизация абонента)
●
Периодические задачи (тайм-ауты)
●
Мониторинг внутреннего состояния
●
Взаимодействие со сторонними системами
(DHCP, Radius, ...)
Thread
Максим Хижинский, C++ CoreHard Autumn 2017
Ядро DPIЯдро DPI
RSS RSS RSS RSS
RSS RSS RSS RSS
RSS RSS RSS RSS
RSS RSS RSS RSS
NIC Application
Thread 0
Thread 1
Thread 2
Thread 3
Thread 4
Thread 5
Thread 6
Thread 7
Thread N
Hash
Apartment modelApartment model
Служебные потоки:Служебные потоки:
Мониторинг, отложенные действия, взаимодействиеМониторинг, отложенные действия, взаимодействие
со сторонними системами, ...со сторонними системами, ...
Максим Хижинский, C++ CoreHard Autumn 2017
Apartment modelApartment model - концепция
●
Поток (thread) — низкоуровневая абстракция,
не связанная с предметной областью
●
Понятия «управление потоками» не должно
быть в принципе
●
ITC (inter-thread communication) — глубоко
спрятано. Вызываем member function, где и
как она выполняется — определяет владелец
Максим Хижинский, C++ CoreHard Autumn 2017
Apartment modelApartment model - концепция
Msg queue
Apartment == thread
Apartment — замкнутая система
Взаимодействие — только через
MPSC-очередь сообщений
Максим Хижинский, C++ CoreHard Autumn 2017
Apartment modelApartment model - концепция
Apartment 1 Apartment 2
MsgQ MsgQ
Апартменты заселяются компонентами
Заселение - на этапе старта, положение компонента
задается в conf-файле
Максим Хижинский, C++ CoreHard Autumn 2017
Apartment modelApartment model - концепция
Apartment 1 Apartment 2
MsgQ MsgQ
Каждый компонент — single threaded
Общение между компонентами — async call
Компоненты не знают о расположении друг друга
Максим Хижинский, C++ CoreHard Autumn 2017
Apartment modelApartment model
class Component {
public:
Component( Apartment& ap ): apartment_( ap ) {}
void foo( int arg1, void* arg2 ) {
if ( current_apartment() != apartment_ )
apartment_.invoke( this, foo, arg1, arg2 );
else {
// находимся в своем апартменте
// выполняем свою работу
}
}
// Вызов — всегда асинхронный
void bar() {
apartment_.invoke( this, do_bar );
}
private:
void do_bar() { … }
private:
Apartment& apartment_; // в какой апартмент заселен
};
Максим Хижинский, C++ CoreHard Autumn 2017
Apartment model - сервисыApartment model - сервисы
Компоненты хотят:
✔Распределять память
✔Выполнять периодические или отложенные действия
✔Следить за внешними событиями (сетевое
взаимодействие)
Apartment 1
MsgQ
Apartment 2
MsgQ
Apartment allocator (single-threaded!)
Apartment model - allocatorApartment model - allocator
Apartment 1
MsgQ
Apartment 2
MsgQ
1
1
2
3
4
A: ptr = ap1.alloc( 64 )
2 A: B.invoke( foo, ptr );
A
B
3 B: foo( ptr ) { ...
4 B: ap1.free( ptr ); }
Apartment model - allocatorApartment model - allocator
Apartment 1
MsgQ
Apartment 2
MsgQ
1
1
A: ptr = ap1.alloc( 64 )
2 A: B.invoke( foo, ptr );
A
3 B: foo( ptr ) { ...
4 B: ap2.free( ptr ); }
B
2
3
4
Apartment model - allocatorApartment model - allocator
Apartment 1
MsgQ
Apartment 2
MsgQ
1
A
4 B: ap2.free( ptr );
B
2
3
4
5
5 ap1.invoke( free, ptr );
6
6 A: ap1.free( ptr );
Apartment allocatorApartment allocator
ТребованияТребования:
●
Избежать фрагментации / вспенивания
●
Для каждого распределенного блока памяти
при вызове free() нужно быстро понять, к
какому аллокатору (апартменту) он
относится
●
Выделенный блок памяти не должен иметь
префиксов / управляющих структур
РешениеРешение: страничный single-threaded
субаллокатор
Максим Хижинский, C++ CoreHard Autumn 2017
Apartment allocatorApartment allocator
Page header
Block 1
Block 1
Block 1
Block N
64 byte blocks
Page header
Block 1
Block 2
Block 3
Block N
128 byte blocks
Page header
Block 1
Block 2
Block 3
Block N
256 byte blocks
Page header
Block 1
Block 2
Block 3
Block N
Page header
Block 1
Block 2
Block 3
Block N
64K byte blocks
1 2 3 k
●
Размер страницы — фиксированный (2M)
●
Для каждой страницы - блоки фиксированного размера
●
Выделение M байт:
●
size_class = ближайшая сверху к M степень двойки
●
Выделяем свободный блок из страницы для size_class
Максим Хижинский, C++ CoreHard Autumn 2017
Apartment allocatorApartment allocator
struct page_header {
size_class_desc* psc; // дескриптор size_class
apartment* owner; // апартмент-владелец страницы
void* first_free_block; // 1-й свободный блок страницы
page_header* next; // связь в списки для size_class
};
struct size_class_desc {
apartment* owner;
page_header* current_page; // с этой страницы выделяем
page_header* part_page_list; // список неполных страниц
page_header* full_page_list; // список полных страниц
};
Размер страницы — 2M (2**21 — Linux huge page), выравненный
по 2M
x x x x 0 0 0
02063
page header
Максим Хижинский, C++ CoreHard Autumn 2017
Apartment model - сервисыApartment model - сервисы
Компоненты хотят:
✔Распределять память - done
✔Выполнять периодические или отложенные действия
✔Следить за внешними событиями (сетевое
взаимодействие)
Apartment 1
MsgQ
Apartment 2
MsgQ
Максим Хижинский, C++ CoreHard Autumn 2017
Apartment model - schedulerApartment model - scheduler
Apartment 1
MsgQ
Apartment 2
MsgQ
Root apartment
MsgQ
Scheduler +
Connection monitor
add fdschedule
Выполнить foo()
в apartment 1
через T ms
(в т.ч. периоди-
чески)
add/remove fd to
connection monitor,
invoke bar() on event
in apartment 2
Apartment model - schedulerApartment model - scheduler
Root apartment
MsgQ
Scheduler +
Connection monitor
epoll_wait()
int epoll_wait(int epfd,
struct epoll_event *events, int maxevents,
int timeout );
Максим Хижинский, C++ CoreHard Autumn 2017
Ядро DPIЯдро DPI
RSS RSS RSS RSS
RSS RSS RSS RSS
RSS RSS RSS RSS
RSS RSS RSS RSS
NIC Application
Thread 0
Thread 1
Thread 2
Thread 3
Thread 4
Thread 5
Thread 6
Thread 7
Thread N
Hash
Shared dataShared data
Hash — не всегда распределит в нужный поток — появляются
shared data между worker threads
Persistent data (например, сессии) — создаются в apartment,
используются в worker threads
Shared dataShared data
По месту созданияПо месту создания:
●
Worker threads — нет аллокатора, на
предраспределенных массивах (pointer =
индекс в массиве)
●
Apartment — есть аллокатор, истинно
динамические контейнеры
Постоянная задачаПостоянная задача: по возможности вывести
создание/удаление shared data в apartment
Максим Хижинский, C++ CoreHard Autumn 2017
Shared dataShared data
Intrusive (in-place) containersIntrusive (in-place) containers
●
std — хранит копии, intrusive — no copy
●
Разделено создание данных и операции с
контейнером
●
Одни и те же данные можно одновременно
хранить в разных контейнерах
пример: boost::intrusiveboost::intrusive
Максим Хижинский, C++ CoreHard Autumn 2017
Intrusive containersIntrusive containers
template <typename Tag = void>
struct slist_node {
slist_node<Tag>* next_;
};
template <typename Tag = void>
struct rbtree_node {
rbtree_node<Tag>* parent_, * left_, * right_;
int color;
};
struct tag_list1 {};
struct tag_list2 {};
struct my_data: slist_node<tag_list1>,
slist_node<tag_list2>,
rbtree_node<>
{
std::string key;
// прочие данные
};
Максим Хижинский, C++ CoreHard Autumn 2017
P – thread count
Thread 0
Thread HP Manager
0
1
…
K - 1
HP[K]
0
1
2
…
R - 1
Retired[R]
Hazard Pointer Singleton
Thread
1
Thread
P - 1
K = 4 R = 2 KP
<K,P, R> : R > K * P
Hazard PointersHazard Pointers
Максим Хижинский, C++ CoreHard Autumn 2017
Удаление элемента
void hp_retire( T * what ) {
push what to current_thread.Retired array
if ( current_thread.Retired is full )
hp.Scan( current_thread );
}
void hp::Scan() {
void * guarded[K*P] = union HP[K] for all P thread;
foreach ( p in current_thread.Retired[R] )
if ( p not in guarded[] )
delete p;
}
<K,P, R> : R > K * P
0
1
2
…
R - 1
Retired[R]
0
1
…
K - 1
HP[K]
Гарантия scan():
Hazard PointersHazard Pointers
Максим Хижинский, C++ CoreHard Autumn 2017
Ядро DPIЯдро DPI
RSS RSS RSS RSS
RSS RSS RSS RSS
RSS RSS RSS RSS
RSS RSS RSS RSS
NIC Application
Thread 0
Thread 1
Thread 2
Thread 3
Thread 4
Thread 5
Thread 6
Thread 7
Thread N
Hash
Hazard PointersHazard Pointers
HP применима, если:
●
Либо Scan() вызывается в apartment, не в worker thread
●
Либо Scan() в worker threads подменяет Retired array на
пустой из пула и передает заполненный Retired array в
apartment
Спасибо за вниманиеСпасибо за внимание
Максим Хижинский, C++ CoreHard Autumn 2017

Обработка потока данных на примере deep packet inspection: внутренняя архитектура и решения

  • 1.
  • 2.
    ПрологПролог 3 вечных вопроса Чтоделать?Что делать? Кто виноват?Кто виноват? Максим Хижинский, C++ CoreHard Autumn 2017
  • 3.
    ПрологПролог 3 вечных вопроса Чтоделать?Что делать? Кто виноват?Кто виноват? Почему такПочему так получилось?получилось? Как сделать?Как сделать? Максим Хижинский, C++ CoreHard Autumn 2017
  • 4.
    4x10G NIC (NetworkInterface Card)4x10G NIC (Network Interface Card) Максим Хижинский, C++ CoreHard Autumn 2017
  • 5.
    4x10G NIC Инструменты: ● Netmap —opensource ● pf_ring — proprietary ● DPDK (Data Plane Dev Kit) — opensource Key features: ● Работа в userspace, минуя kernel ● DMA, no copy Максим Хижинский, C++ CoreHard Autumn 2017
  • 6.
    4x10G NIC4x10G NIC Пакет= 512 байт = 2**9 байт = 2**17 бит Поток = 40 x 2**30 бит Итого: 40 x 2**30 / 2**17 = 40 x 2**13 пакетов/сек Процессор = 4 ГГц ~ 4 x 2**30 На пакет: 4 x 2**30 / 40 x 2**13 = 0.1 x 2**17 ~ 2**14 = 16K тактов на пакет Максим Хижинский, C++ CoreHard Autumn 2017
  • 7.
    Port 0 RSS RSSRSS RSS Port 3 RSS RSS RSS RSS Port 1 RSS RSS RSS RSS Port 2 RSS RSS RSS RSS NIC Application Thread 0 Thread 1 Thread 2 Thread 3 Thread 4 Thread 5 Thread 6 Thread 7 Thread N Hash
  • 8.
    Порочные практикиПорочные практики Безумноеколичество потоков — потоков много больше, чем ядер Создание потоков «на лету», под выполнение конкретного действия Максим Хижинский, C++ CoreHard Autumn 2017
  • 9.
    Распараллеливание Thread Thread ThreadThread Thread Thread Число потоков <= число ядер
  • 10.
    Распараллеливание Thread Thread ThreadThread Thread Core 0 — свободен (для OS) Hard pinning threads to cores
  • 11.
    Worker thread ХарактеристикиХарактеристики: ● No memoryallocation ● Hard pinned to processor core ● No memcpy (по возможности) Thread In port Out port Максим Хижинский, C++ CoreHard Autumn 2017
  • 12.
    Worker thread Характеристики: ● No memoryallocation (= no std containers) ● Hard pinned to processor core ● No memcpy (по возможности) ПроблемыПроблемы: ● Длительные/отложенные действия (авторизация абонента) ● Периодические задачи (тайм-ауты) ● Мониторинг внутреннего состояния ● Взаимодействие со сторонними системами (DHCP, Radius, ...) Thread Максим Хижинский, C++ CoreHard Autumn 2017
  • 13.
    Ядро DPIЯдро DPI RSSRSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS NIC Application Thread 0 Thread 1 Thread 2 Thread 3 Thread 4 Thread 5 Thread 6 Thread 7 Thread N Hash Apartment modelApartment model Служебные потоки:Служебные потоки: Мониторинг, отложенные действия, взаимодействиеМониторинг, отложенные действия, взаимодействие со сторонними системами, ...со сторонними системами, ... Максим Хижинский, C++ CoreHard Autumn 2017
  • 14.
    Apartment modelApartment model- концепция ● Поток (thread) — низкоуровневая абстракция, не связанная с предметной областью ● Понятия «управление потоками» не должно быть в принципе ● ITC (inter-thread communication) — глубоко спрятано. Вызываем member function, где и как она выполняется — определяет владелец Максим Хижинский, C++ CoreHard Autumn 2017
  • 15.
    Apartment modelApartment model- концепция Msg queue Apartment == thread Apartment — замкнутая система Взаимодействие — только через MPSC-очередь сообщений Максим Хижинский, C++ CoreHard Autumn 2017
  • 16.
    Apartment modelApartment model- концепция Apartment 1 Apartment 2 MsgQ MsgQ Апартменты заселяются компонентами Заселение - на этапе старта, положение компонента задается в conf-файле Максим Хижинский, C++ CoreHard Autumn 2017
  • 17.
    Apartment modelApartment model- концепция Apartment 1 Apartment 2 MsgQ MsgQ Каждый компонент — single threaded Общение между компонентами — async call Компоненты не знают о расположении друг друга Максим Хижинский, C++ CoreHard Autumn 2017
  • 18.
    Apartment modelApartment model classComponent { public: Component( Apartment& ap ): apartment_( ap ) {} void foo( int arg1, void* arg2 ) { if ( current_apartment() != apartment_ ) apartment_.invoke( this, foo, arg1, arg2 ); else { // находимся в своем апартменте // выполняем свою работу } } // Вызов — всегда асинхронный void bar() { apartment_.invoke( this, do_bar ); } private: void do_bar() { … } private: Apartment& apartment_; // в какой апартмент заселен }; Максим Хижинский, C++ CoreHard Autumn 2017
  • 19.
    Apartment model -сервисыApartment model - сервисы Компоненты хотят: ✔Распределять память ✔Выполнять периодические или отложенные действия ✔Следить за внешними событиями (сетевое взаимодействие) Apartment 1 MsgQ Apartment 2 MsgQ Apartment allocator (single-threaded!)
  • 20.
    Apartment model -allocatorApartment model - allocator Apartment 1 MsgQ Apartment 2 MsgQ 1 1 2 3 4 A: ptr = ap1.alloc( 64 ) 2 A: B.invoke( foo, ptr ); A B 3 B: foo( ptr ) { ... 4 B: ap1.free( ptr ); }
  • 21.
    Apartment model -allocatorApartment model - allocator Apartment 1 MsgQ Apartment 2 MsgQ 1 1 A: ptr = ap1.alloc( 64 ) 2 A: B.invoke( foo, ptr ); A 3 B: foo( ptr ) { ... 4 B: ap2.free( ptr ); } B 2 3 4
  • 22.
    Apartment model -allocatorApartment model - allocator Apartment 1 MsgQ Apartment 2 MsgQ 1 A 4 B: ap2.free( ptr ); B 2 3 4 5 5 ap1.invoke( free, ptr ); 6 6 A: ap1.free( ptr );
  • 23.
    Apartment allocatorApartment allocator ТребованияТребования: ● Избежатьфрагментации / вспенивания ● Для каждого распределенного блока памяти при вызове free() нужно быстро понять, к какому аллокатору (апартменту) он относится ● Выделенный блок памяти не должен иметь префиксов / управляющих структур РешениеРешение: страничный single-threaded субаллокатор Максим Хижинский, C++ CoreHard Autumn 2017
  • 24.
    Apartment allocatorApartment allocator Pageheader Block 1 Block 1 Block 1 Block N 64 byte blocks Page header Block 1 Block 2 Block 3 Block N 128 byte blocks Page header Block 1 Block 2 Block 3 Block N 256 byte blocks Page header Block 1 Block 2 Block 3 Block N Page header Block 1 Block 2 Block 3 Block N 64K byte blocks 1 2 3 k ● Размер страницы — фиксированный (2M) ● Для каждой страницы - блоки фиксированного размера ● Выделение M байт: ● size_class = ближайшая сверху к M степень двойки ● Выделяем свободный блок из страницы для size_class Максим Хижинский, C++ CoreHard Autumn 2017
  • 25.
    Apartment allocatorApartment allocator structpage_header { size_class_desc* psc; // дескриптор size_class apartment* owner; // апартмент-владелец страницы void* first_free_block; // 1-й свободный блок страницы page_header* next; // связь в списки для size_class }; struct size_class_desc { apartment* owner; page_header* current_page; // с этой страницы выделяем page_header* part_page_list; // список неполных страниц page_header* full_page_list; // список полных страниц }; Размер страницы — 2M (2**21 — Linux huge page), выравненный по 2M x x x x 0 0 0 02063 page header Максим Хижинский, C++ CoreHard Autumn 2017
  • 26.
    Apartment model -сервисыApartment model - сервисы Компоненты хотят: ✔Распределять память - done ✔Выполнять периодические или отложенные действия ✔Следить за внешними событиями (сетевое взаимодействие) Apartment 1 MsgQ Apartment 2 MsgQ Максим Хижинский, C++ CoreHard Autumn 2017
  • 27.
    Apartment model -schedulerApartment model - scheduler Apartment 1 MsgQ Apartment 2 MsgQ Root apartment MsgQ Scheduler + Connection monitor add fdschedule Выполнить foo() в apartment 1 через T ms (в т.ч. периоди- чески) add/remove fd to connection monitor, invoke bar() on event in apartment 2
  • 28.
    Apartment model -schedulerApartment model - scheduler Root apartment MsgQ Scheduler + Connection monitor epoll_wait() int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout ); Максим Хижинский, C++ CoreHard Autumn 2017
  • 29.
    Ядро DPIЯдро DPI RSSRSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS NIC Application Thread 0 Thread 1 Thread 2 Thread 3 Thread 4 Thread 5 Thread 6 Thread 7 Thread N Hash Shared dataShared data Hash — не всегда распределит в нужный поток — появляются shared data между worker threads Persistent data (например, сессии) — создаются в apartment, используются в worker threads
  • 30.
    Shared dataShared data Поместу созданияПо месту создания: ● Worker threads — нет аллокатора, на предраспределенных массивах (pointer = индекс в массиве) ● Apartment — есть аллокатор, истинно динамические контейнеры Постоянная задачаПостоянная задача: по возможности вывести создание/удаление shared data в apartment Максим Хижинский, C++ CoreHard Autumn 2017
  • 31.
    Shared dataShared data Intrusive(in-place) containersIntrusive (in-place) containers ● std — хранит копии, intrusive — no copy ● Разделено создание данных и операции с контейнером ● Одни и те же данные можно одновременно хранить в разных контейнерах пример: boost::intrusiveboost::intrusive Максим Хижинский, C++ CoreHard Autumn 2017
  • 32.
    Intrusive containersIntrusive containers template<typename Tag = void> struct slist_node { slist_node<Tag>* next_; }; template <typename Tag = void> struct rbtree_node { rbtree_node<Tag>* parent_, * left_, * right_; int color; }; struct tag_list1 {}; struct tag_list2 {}; struct my_data: slist_node<tag_list1>, slist_node<tag_list2>, rbtree_node<> { std::string key; // прочие данные }; Максим Хижинский, C++ CoreHard Autumn 2017
  • 33.
    P – threadcount Thread 0 Thread HP Manager 0 1 … K - 1 HP[K] 0 1 2 … R - 1 Retired[R] Hazard Pointer Singleton Thread 1 Thread P - 1 K = 4 R = 2 KP <K,P, R> : R > K * P Hazard PointersHazard Pointers Максим Хижинский, C++ CoreHard Autumn 2017
  • 34.
    Удаление элемента void hp_retire(T * what ) { push what to current_thread.Retired array if ( current_thread.Retired is full ) hp.Scan( current_thread ); } void hp::Scan() { void * guarded[K*P] = union HP[K] for all P thread; foreach ( p in current_thread.Retired[R] ) if ( p not in guarded[] ) delete p; } <K,P, R> : R > K * P 0 1 2 … R - 1 Retired[R] 0 1 … K - 1 HP[K] Гарантия scan(): Hazard PointersHazard Pointers Максим Хижинский, C++ CoreHard Autumn 2017
  • 35.
    Ядро DPIЯдро DPI RSSRSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS RSS NIC Application Thread 0 Thread 1 Thread 2 Thread 3 Thread 4 Thread 5 Thread 6 Thread 7 Thread N Hash Hazard PointersHazard Pointers HP применима, если: ● Либо Scan() вызывается в apartment, не в worker thread ● Либо Scan() в worker threads подменяет Retired array на пустой из пула и передает заполненный Retired array в apartment
  • 36.
    Спасибо за вниманиеСпасибоза внимание Максим Хижинский, C++ CoreHard Autumn 2017