vak: (Default)
Serge Vakulenko ([personal profile] vak) wrote2025-02-18 06:23 pm

Асинхронный линукс

Народ, кто-нибудь уже поимел опыт с <linux/io_uring.h>? Как оно по жизни?

Неожиданно для себя обнаружил, что в Линуксе пять лет назад появилась крутая фича. А именно три системных вызова, реализующих эффективный асинхронный интерфейс ко всем сервисам ядра.
  • int io_uring_setup(unsigned entries, struct io_uring_params *p);
  • int io_uring_enter(unsigned fd, unsigned to_submit, unsigned min_complete, unsigned flags, sigset_t *sig);
  • int io_uring_register(unsigned fd, unsigned opcode, void *arg, unsigned nr_args);
Революционная штука, как я погляжу. Может коренным образом изменить подход к разработке приложений. Только сложновато для программиста выходит. Есть статьи про это дело.
spamsink: (Default)

[personal profile] spamsink 2025-02-19 05:12 am (UTC)(link)
Опоздала идея лет на 30-40, когда дисководы головками медленно двигали. Тогда выигрыш был бы ещё более впечатляющим.
lev: (Default)

[personal profile] lev 2025-02-19 07:46 pm (UTC)(link)
double buffering легко делался в ос/ес на ПЛ/1
помнится с приятелем мутили печать с ленты, в один буфер читалось, другой форматировался, третий выводился (без спулера)

[personal profile] sassa_nf 2025-02-19 07:55 pm (UTC)(link)
How so?

Slow disk => perf is dominated by io wait, you can ignore context switch + buffer copying cost.
spamsink: (Default)

[personal profile] spamsink 2025-02-20 05:36 am (UTC)(link)
When multiple I/O requests are sent to the driver, the driver would order them according to the current position of the head and the locations of the blocks of the file, minimizing io wait.
lev: (Default)

[personal profile] lev 2025-02-19 07:09 am (UTC)(link)
отлично, будем выполнять ио в кернеле.
радость хакерам.
lev: (Default)

[personal profile] lev 2025-02-19 07:40 pm (UTC)(link)
я че-то туплю. оно сделано чтобы поллить завершение операции без syscall?
lev: (Default)

[personal profile] lev 2025-02-19 08:05 pm (UTC)(link)
джизус крайст. а async io без поллинга никак? не признают богомерзких threads?
lev: (Default)

[personal profile] lev 2025-02-19 08:30 pm (UTC)(link)
пардон, опять туплю. контекст переключается медленне, чем приходят байты с конечного устройства?
Edited 2025-02-19 20:30 (UTC)
lev: (Default)

[personal profile] lev 2025-02-19 08:50 pm (UTC)(link)
интересно, i286 успевал обрабатывать внешний модем на com порту с 56к/сек (там же вроде бы прерывание на байт).
lev: (Default)

[personal profile] lev 2025-02-19 09:07 pm (UTC)(link)
ok, даже с фифо, если прерывание на половине буфера, это 7к/сек
lev: (Default)

[personal profile] lev 2025-02-19 09:27 pm (UTC)(link)
все таки, меня удивляет такое медленное переключение контекста.

[personal profile] sassa_nf 2025-02-20 08:27 pm (UTC)(link)
It's not just speed of context switch. If you have 10M connections, you just can't have thread-per-connection. So you need ways of processing orders of magnitude more connections than you have threads.
x86128: (Default)

[personal profile] x86128 2025-02-19 04:09 pm (UTC)(link)
не не, там в юзерспейсе всё. наоборот это безопасней и меньше копирований из пустого в порожнее

[personal profile] chabapok 2025-02-19 08:34 am (UTC)(link)
если за 1 переключение контекста хочется обработать несколько событий, то нужно сначала некоторое время, чтобы эти события накопились. В течении этого времени событие, которое можно было бы обработать, лежит и ждет пока очередь заполнится, или таймаута.

Хм.

Наверное, это хорошо с точки зрения снижения энергопотребления, дипломатически выражаясь.

Но опять же - я не пробовал этот механизм. Это вот такое суждение навскидку. Может я и ерунду написал.

ап: кстати вот, есть же задачи, где высокая отзывчивость не на первом месте, и ей можно пожертвовать в ущерб производительности. Я таких задач вобщем-то не встречал, но они бывают.
Edited 2025-02-19 08:51 (UTC)

[personal profile] ichthuss 2025-02-19 10:41 am (UTC)(link)
Не обязательно ждать. Несколько собітий могут и так накопиться за то время, пока мі обрабатівали предідущий возврат из системного візова. В єтом случае мі получаем несколько собітий за один системній візов и с нулевой латентностью.

[personal profile] chabapok 2025-02-19 03:26 pm (UTC)(link)
> Несколько собітий могут и так накопиться за то время, пока мі обрабатівали предідущий возврат из системного візова

там эта очередь - многопоточная, что ли? Один поток уже обрабатывает возврат, а другой поток все еще может в эту очередь что-то добавить. А потом хоба - и уже в какой-то момент считатется, что возврат произошел, и события больше не добавляются в эту очередь. Например таким моментом может быть обращение к size(). Ну то есть, начало работы с элементами.

Ну, вприницпе, да. Полезно.
Edited 2025-02-19 15:35 (UTC)

[personal profile] ichthuss 2025-02-20 08:36 am (UTC)(link)
Не уверен, что понял ваш комментарий. Суть именно в том, чтобі в одном треде обрабатівать много одновременніх операций (которіе традиционно бі обрабатівались в разніх тредах). Т.е., условно говоря, возможен подобная последовательность собітий:
- мі имеем сокеті к 100к клиентов. сабмитим 100к запросов на чтение одним сисколлом, ждем ответа
- получаем ответ, обрабатіваем запрос клиента, формируем ответ, сабмитим респонз
- за время, пока мі обрабатівали, пришло еще 10 запросов от клиента, они сразу нам доступні для обработки, уже лежать в кольцевом буфере - пока мі обрабатівали запрос в юзерспейсе, ядро тоже времени не теряло
- мі последовательно обрабатіваем єти запросі, за єто время заканчивается обработка отправки нашего респонза первому клиенту, и єто собітие тоже попадает в буфер
- когда очередь доходит до обработки собітия завершения передачи респонза, мі в ответ опять сабмитим запрос на чтение из єтого сокета.

Ну и т.д. Я не уверен, что ві именно єто имели ввиду под "многопоточной очередью".

[personal profile] chabapok 2025-02-20 11:39 am (UTC)(link)
нет, я не єто имел в виду.

для начала мне уже ясно: надо либо читать документацию - либо мы говорим, что пофантазируем как оно могло бы работать правильно. Сейчас мы занимаемся вторым. Надо это понимать.

> за время, пока мі обрабатівали, пришло еще 10 запросов от клиента, они сразу нам доступні для обработки,

а вот єто без многопотока невозможно. То есть тут есть всего 2 варианта: либо мы делаем некий вызов, возвращающий новые события. И тогда єто 1 поток. Либо же мы таких вызовов не делаем, а наша входящая очередь многопоточная, и читатель в ней один (мы), а все писатели в ядре и их много (сокеты, клики мышкой и тд).

> Суть именно в том, чтобі в одном треде обрабатівать много одновременніх операций

а чем это принципиально от epoll отличается? В epoll ты сделал вызов - а тебе возвращается список сокетов, по которым есть обновление (данные для чтения, или появившаяся возможность записать еще если это tcp).

Кстати. Когда сетевая карточка генерирует прерывание, то по умолчанию оно приходит на любое ядро. (можно настроить, запретив какое-то ядро - но нужно ли?) Соответственно, получается задача, что данные из нескольких потоков надо свести в один. Зачем так сделано, я не очень понимаю. Как показала практика, не всегда с первого раза архитектуры делают удачно. С другой же стороны, те кто это делал, явно знали больше, чем я. Тут я затрудняюсь дать оценку. Наверное в каких-то случаях это хорошо, а в каких-то других нет.

[personal profile] chabapok 2025-02-19 03:18 pm (UTC)(link)
ой, опечатка: "и ей можно пожертвовать в ущерб производительности" -> "и ей можно пожертвовать за счет увеличения производительности"

[personal profile] ichthuss 2025-02-19 10:50 am (UTC)(link)
Интересно, как именно єта штука дружит с многопоточностью. Обрабатівать данніе в одном вісокопроизводительном треде - єто, разумеется, круто, но может біть недостаточно.

[personal profile] sassa_nf 2025-02-19 07:52 pm (UTC)(link)
The whole idea is that you are going to have more fds than threads.

Eg 10M sockets >> 32 CPU_count.

[personal profile] ichthuss 2025-02-20 08:18 am (UTC)(link)
Єто понятно. Речь о том, что в приводиміх примерах описано, как обрабатівать, скажем, 1М сокетов в одном треде, но не о том, как обрабатівать 16М сокетов в 16 тредах на 16 ядрах. Как именно предлагается єто реализовать? Один ринг-буфер на все треді с безблокировочнім извлечением єлемента? По одному ринг-буферу на каждій тред (и как тогда решается проблема длинной обработки одного возврата, из-за которой застопорится обработка всех дескрипторов, віпавших на єтот тред)? Что-то промежуточное?

[personal profile] sassa_nf 2025-02-20 01:42 pm (UTC)(link)
That's left as exercise to the reader.

If you used epoll, you'll notice that io_uring is not that different. The user of the API needs to figure out how to share work, how to associate execution context with IO requests and how to manage system-wide progress.

For example, if it is a Async framework, the execution context is a Future or a Promise of the IO result.

Spreading work among threads can be via hashing and periodic rehashing, work stealing, etc, really task-specific. But if you are able to saturate CPU without that, then you can shrug it off as unnecessary.

[personal profile] dedekha 2025-02-19 01:13 pm (UTC)(link)
Все это очень хорошо и действительно решает проблемы однако очень быстро ведет к сложным fsm. A по моим ненаучным но долгосрочным наблюдениям сложные fsm недоступны среднему программисту. Интересно может ли помочь ИИ?

[personal profile] dedekha 2025-02-20 12:23 am (UTC)(link)
Дело в аппликации. Никто не сможет справиться матрицей 1000x1000 state/event если у большинства проблемы с 10x10.

https://www.google.com/search?q=state+explosion+problem&oq=state+expl&gs_lcrp=EgRlZGdlKgcIAhAAGIAEMgYIABBFGDkyBwgBEAAYgAQyBwgCEAAYgAQyBwgDEAAYgAQyBwgEEAAYgAQyBwgFEAAYgAQyBwgGEAAYgAQyBwgHEAAYgAQyBwgIEAAYgATSAQg5NDkzajBqMagCALACAQ&sourceid=chrome&ie=UTF-8

[personal profile] ichthuss 2025-02-20 08:22 am (UTC)(link)
В общем-то, теоретически ничто не мешает компилятору преобразовать код, написанній как для тредов, в код, написанній для асинка. Просто одна длинная процедура разбивается на куски-колбєки. Другое дело, подходят ли для єтого распространенніе язіки. Вроде бі концептуально Єрланг для чего-то подобного создавался, может, подойдет, но не уверен - сам не пробовал.

[personal profile] dedekha 2025-02-20 12:46 pm (UTC)(link)
В синхроном коде можно иметь 1000 кусков типа: послать сообщение, ждать ответа, обработать ответ (*).

При прямом конвертировании в асинхронный код каждый такой кусок добавляет один state и один event к общей матрице.

(*) Простота и наглядность такого кода одна из главных причин почему люди мучаются с много-поточностью.

[personal profile] sassa_nf 2025-02-19 07:50 pm (UTC)(link)
epoll done right, and support for arbitrary fd, not just network sockets.