vak: (Default)
[personal profile] vak
Народ, кто-нибудь уже поимел опыт с <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);
Революционная штука, как я погляжу. Может коренным образом изменить подход к разработке приложений. Только сложновато для программиста выходит. Есть статьи про это дело.

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

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

Date: 2025-02-19 19:55 (UTC)
From: [personal profile] sassa_nf
How so?

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

Date: 2025-02-20 05:36 (UTC)
spamsink: (Default)
From: [personal profile] spamsink
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.

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

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

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

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

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

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

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

Date: 2025-02-20 20:27 (UTC)
From: [personal profile] sassa_nf
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.

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

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

Хм.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Eg 10M sockets >> 32 CPU_count.

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

Date: 2025-02-20 13:42 (UTC)
From: [personal profile] sassa_nf
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.

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

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

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

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

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

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

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

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