Клиент — сервер. Клиент соединяется с сервером , передает данные ( всего 2 байта ) и отсоединяется. Сервер, приняв данные, сохраняет их в некоей структуре в памяти(кастомный вариант дерева) .
Клиентов много ( сотни-тысячи), подключения частые ( неск раз в секунду каждый ).
Допустим на сервере 64 ядра — как лучше распараллелить выполнение?
Вариант 1:
единственный сокет, обрабатываемый одним потоком — быстро принять данные от клиента, быстро кинуть куда-нибудь ( напр. в очередь ) и быть готовым принять подключение от следующего клиента... а саму обработку данных ( вставку в дерево) ,которая может занять много времени, выполнять многопоточно ( например пул потоков выбирает данные из очереди и вставляет в дерево — в некоторых случаях это может происходить lock-free, когда параллельно работающие потоки вставляют данных в разные куски дерева )
Вариант 2:
много сокетов, каждый обрабатывается своим потоком, при подключении к каждому сокету выполняется весь цикл обработки данных — очевидно в этом случает время "недоступности" сокета для след клиента увеличится, но самих сокетов будет больше (наверное имеет смысл по количеству ядер процессора )
В каком варианте пропускная способность будет выше?
PS. Какие нынче фрэймворки используются в С++ чтобы руками не писать все эти детали подключения, сокетов, буфферов и т.д ( желательно уровень абстракции повыше ) ? Или может уже в стандарте С++ есть что-то для этих целей ?
O>Клиент — сервер. Клиент соединяется с сервером , передает данные ( всего 2 байта ) и отсоединяется. Сервер, приняв данные, сохраняет их в некоей структуре в памяти(кастомный вариант дерева) .
Классический пул потоков размером в два раза больше, чем имеется ядер, IoCompletionPort и AcceptEx. Писать это муторно, но работать будет быстрее чего бы то ни было.
O>Клиент — сервер. Клиент соединяется с сервером , передает данные ( всего 2 байта ) и отсоединяется. Сервер, приняв данные, сохраняет их в некоей структуре в памяти(кастомный вариант дерева) . O>Клиентов много ( сотни-тысячи), подключения частые ( неск раз в секунду каждый ). O>Допустим на сервере 64 ядра — как лучше распараллелить выполнение?
1. соединения нужно держать либо постоянными (иначе всё будет умирать на tcp handshake) либо ( особенно, если это телеметрия ) использовать UDP
2. забирать данные с одного сокета асинхронным epoll/completion ports и складывать их в lock-free очередь
3. для дерева нужно использовать skip-list, но есть мнение что если все 64 ядра начнут читать-писать в него то даже skip-list будет выдавать никакой перформанс. Я бы начал с одного потока, модифицирующего дерево. 100 тысяч вставок/удалений в секунду оно в любом случае сможет на нормальном железе. А уже логику работы с деревянными данными запускал бы в других потоках.
O>PS. Какие нынче фрэймворки используются в С++ чтобы руками не писать все эти детали подключения, сокетов, буфферов и т.д ( желательно уровень абстракции повыше ) ? Или может уже в стандарте С++ есть что-то для этих целей ?
boost.asio, boost.lockfree, boost.thread
Здравствуйте, antropolog, Вы писали:
O>>Клиентов много ( сотни-тысячи), подключения частые ( неск раз в секунду каждый ).
A>1. соединения нужно держать либо постоянными (иначе всё будет умирать на tcp handshake) либо ( особенно, если это телеметрия ) использовать UDP A>2. забирать данные с одного сокета асинхронным epoll/completion ports и складывать их в lock-free очередь A>3. для дерева нужно использовать skip-list, но есть мнение что если все 64 ядра начнут читать-писать в него то даже skip-list будет выдавать никакой перформанс. Я бы начал с одного потока, модифицирующего дерево. 100 тысяч вставок/удалений в секунду оно в любом случае сможет на нормальном железе. А уже логику работы с деревянными данными запускал бы в других потоках.
Как я понял клиентов 100 — несколько тысяч. Зачем там всякие извращения с перформансом, когда и по идее и так из коробки должно работать.
Берем, например, кросплатформенный asio. Слушаем на одном сокете. Пишем данные очередь. Другие потоки очередь разбирает и пишут уже в нужные структуры данных.