У меня есть приложение на Rust, которое ожидает события в узком цикле. Всякий раз, когда происходит событие, одна и та же полезная нагрузка должна быть записана в несколько сокетов TCP. В настоящее время я использую стандартные неблокирующие сокеты и просто перебираю все сокеты, вызывая запись для каждого из них.
Этот подход работает правильно, но производительность снижается по мере увеличения количества сокетов; общая задержка растет примерно линейно с увеличением количества сокетов.
Я предполагал, что io_uring может улучшить этот сценарий. Идея заключалась в том, чтобы поставить все операции записи (в идеале без копирования данных) в очередь отправки, а затем отправить их с помощью одного системного вызова.
Я реализовал прототип, основанный на этой идее, но результаты были неожиданными: он значительно медленнее, чем неблокирующий цикл записи, а производительность еще больше снижается по мере увеличения количества запросов на запись в очереди.
Ниже приведена соответствующая часть цикла с использованием io_uring:
Код: Выделить всё
let mut ring = IoUring::new(1024)?;
// ...setup accepting tcp sockets...
let mut next_message_time = current_nano_time() + ONE_SECOND;
loop {
if next_message_time < current_nano_time() {
let message = format!("{}\n", next_message_time);
next_message_time = current_nano_time() + ONE_SECOND;
// start pushing send request to the queue
let time_start = current_nano_time();
let mut count = 0;
for connection in connections.iter_mut() {
if connection.state == State::Active {
push_send(&mut ring, connection, &message);
count += 1;
}
}
if count == 0 {
continue;
}
let time_pushed = current_nano_time();
// submit pushed requests
ring.submit().expect("submit returned error");
let time_submitted = current_nano_time();
println!(
"latencies for {} entries: push={}us, submit={}us",
count,
(time_pushed - time_start) / 1000,
(time_submitted - time_pushed) / 1000
);
}
let cqe = ring.completion().next();
if cqe.is_none() {
yield_now();
continue;
}
// ...handle cqe...
}
Вывод журнала выглядит следующим образом:
Код: Выделить всё
new client connected
latencies for 1 entries: push=0us, submit=57us
latencies for 1 entries: push=0us, submit=33us
new client connected
latencies for 2 entries: push=0us, submit=32us
latencies for 2 entries: push=0us, submit=47us
new client connected
latencies for 3 entries: push=0us, submit=49us
latencies for 3 entries: push=0us, submit=72us
new client connected
latencies for 4 entries: push=0us, submit=66us
latencies for 4 entries: push=0us, submit=59us
new client connected
latencies for 5 entries: push=0us, submit=72us
latencies for 5 entries: push=0us, submit=109us
Ожидается ли такое поведение? Я неправильно использую io_uring или мое понимание его характеристик производительности в этом случае неверно?
Подробнее здесь: https://stackoverflow.com/questions/798 ... ket-writes
Мобильная версия