Обработка потока данных на примере deep packet inspection:...
TRANSCRIPT
Максим Хижинский, 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 (по возможности)
ThreadIn 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 0Thread 1Thread 2Thread 3Thread 4Thread 5Thread 6Thread 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 modelclass 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 - allocatorApartment 1
MsgQ
Apartment 2
MsgQ
1
1
2
3
4
A: ptr = ap1.alloc( 64 )
2 A: B.invoke( foo, ptr );
AB
3 B: foo( ptr ) { ...
4 B: ap1.free( ptr ); }
Apartment model - allocatorApartment model - allocatorApartment 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 - allocatorApartment 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 allocatorstruct 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 002063
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 toconnection monitor,invoke bar() on event
in apartment 2
Apartment model - schedulerApartment model - schedulerRoot 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 0Thread 1Thread 2Thread 3Thread 4Thread 5Thread 6Thread 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 containerstemplate <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
012…
R - 1
Retired[R]
01…
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 0Thread 1Thread 2Thread 3Thread 4Thread 5Thread 6Thread 7
Thread N
Hash
Hazard PointersHazard Pointers
HP применима, если:● Либо Scan() вызывается в apartment, не в worker thread● Либо Scan() в worker threads подменяет Retired array на
пустой из пула и передает заполненный Retired array в apartment
Спасибо за вниманиеСпасибо за внимание
Максим Хижинский, C++ CoreHard Autumn 2017