Я знаю, что есть много вопросов по этому поводу, но я все еще не мог найти ответ, который помогает мне.
Давайте возьмем небольшой tcp-сервер с epoll и хотим, чтобы он использовал как можно больше ядер ЦП. Я думал о двух способах, которыми это могло быть сделано, но ни один из них не работал действительно хорошо.
1 — Каждый поток имеет свой собственный epoll fd и в цикле while (1) использует epoll_wait () и обрабатывает запросы.
2 — только один epoll fd и создание нового потока для каждого запроса при его обработке.
В одном потоке я мог сделать около 25 тыс. Запросов в секунду, поэтому я предполагал, что первый метод очень поможет, но в действительности, когда я использовал 2 epoll fd, приложение могло обрабатывать только ~ 10 тыс. Запросов / с. Очевидно, я даже не считал второй метод реальным, он должен был потерпеть неудачу, так что да.
В общем, мой вопрос: как мне реализовать многопоточность, чтобы она могла действительно использовать больше процессорных ядер?
Сокет неблокируемый, TCP_NODELAY, TCP_FASTOPEN установлены, и я также использую режим EPOLLET.
Чтобы использовать несколько ядер, вам нужно разделить процесс на разные потоки, и каждый поток ожидает своего дескриптора файла. Однако в этом случае, если вы ожидаете только не замужем файловый дескриптор, просто многопоточность и использование блокирующих чтений для каждого файлового дескриптора может быть более эффективным. Вы также можете привязать разные потоки к разным ядрам, так как планировщик часто пытается разместить разные потоки на так же core (потому что их TLB одинаковы), поэтому используйте:
int sched_setaffinity(pid_t pid,size_t cpusetsize,cpu_set_t *mask);
Поможет ли вам вещи аффинно Очевидно, что если у вас больше FD, чем процессоров, вам придется пойти на компромисс.
Вот что я думаю: если у вас есть два потока (как вы пробовали), которые не привязаны к процессору, но в основном ожидают ввода-вывода, планировщик подумает: «Они оба имеют одинаковую площадь TLB и оба просто ожидают I / O — так что я просто оставлю их обоих на одном процессоре «. Это логичная вещь, которая даст хорошую производительность процессора и кеша, но вам нужно меньше задержки, чем эта (потому что, грубо говоря, OPS / sec = 1 / Latency) — так что закрепите эти два потока на разных ядрах с помощью команды выше — at хотя бы посмотри что он делает.
Пожалуйста, поподробнее о том, как обрабатываются данные с вашим первым вариантом, есть ли синхронизация данных между fd (s)? Может быть, это то, что снижает общую производительность.
А для другого варианта, более разумный путь — использовать 1 epollfd и вызывать epoll_wait для него в нескольких потоках. Это немного сложнее, но может дать лучшую производительность для абсолютно связанных приложений, если нет никакой (или небольшой) зависимости данных между fd (s).