roc-streaming / roc-toolkit

Real-time audio streaming over the network.
https://roc-streaming.org
Mozilla Public License 2.0
1.06k stars 213 forks source link

UDP send optimization #348

Closed gavv closed 4 years ago

gavv commented 4 years ago

Task

When a new outgoing packet is created on sender pipeline, it is passed to netio::UdpSenderPort::write(), which adds the packet to the outgoing queue and wakes up libuv thread by raising write_sem_. Then, write_sem_cb_() is called on libuv thread, fetches the packet from the queue, and asks libuv to send it by calling uv_udp_send().

Such implementation allows to keep write() non-blocking even if the socket is not ready for write yet. libuv will wait until the socket becomes ready and then write the packet to it.

However, in most cases the socket is actually ready for write and we could reduce the latency a bit by avoiding an extra thread switch.

To do this, we can try to do a non-blocking sendto() directly in write(). If it succeeds, we're lucky and the packet is passed to kernel immediately without extra thread switch. And if it returns EAGAIN, we can fallback to the current implementation (add to queue and raise write_sem_).

Implementation

  1. Implement a new function bool netio::sendto_nb(int fd, const void* buf, size_t bufsz, const address::Address& dst_addr). It will be a cross-platform wrapper for non-blocking sendto() syscall, for now implemented only for POSIX. It should return true on success. The implementation should be placed into roc_netio/target_posix/roc_netio/sendto.h and roc_netio/target_posix/roc_netio/sendto.cpp.

  2. Add uv_os_fd_t fd_ field to netio::UdpSenderPort. Fill it in open() using uv_fileno(). We can't use libuv handles from other threads, so we should store the raw socket handle instead.

  3. Add the actual optimization to UdpSenderPort::write(). Before locking the mutex, check if the pending packet counter is zero. If it is, try to call sendto_nb() (use saved fd_, the provided packet, and it's destination address). If it succeeded, return; otherwise, fall back to the usual implementation (enqueue a packet and wake up thread).

DColadas commented 4 years ago

Hey! I have had a look at the project and I am interested. May I work on this issue?

gavv commented 4 years ago

Great, you're welcome!

gavv commented 4 years ago

Merged!