Skip to content

Add sendmmsg support for UDP#1034

Open
davidBar-On wants to merge 1 commit intoesnet:masterfrom
davidBar-On:iss873
Open

Add sendmmsg support for UDP#1034
davidBar-On wants to merge 1 commit intoesnet:masterfrom
davidBar-On:iss873

Conversation

@davidBar-On
Copy link
Contributor

@davidBar-On davidBar-On commented Aug 9, 2020

  • Version of iperf3 (or development branch, such as master or
    3.1-STABLE) to which this pull request applies:
    3.10.1 latest master

  • Issues fixed (if any):
    UDP throughput issue #873

  • Brief description of code changes (suitable for use as a commit message):

Add sendmmsg support for sending UDP messages for enhanced throughput.
sendmmsg is used by setting the -Z option (which is currently used only for TCP), as it is regarded as the UDP's alternative to TCP's zero copy.
The number of packets that are send by each call to sendmmsg is the burst size set by the -b option.

Note:

  1. configure.ac was changed so running bootstrap.sh; configure is required for the changes to take effect. (New defines are HAVE_SENDMMSG, HAVE_RECVMMSG and HAVE-SEND_RECVMMSG.)
  2. recvmmsg is not used because tests showed does not help the throughput and event may hart it. However, the changes for testing recvmmsg are commented out in iperf_udp_recv() and not removed in case further evaluation is desired. If this is not the case, then all changes tp iperf_udp_recv can be removed.

@bmah888 bmah888 linked an issue Sep 11, 2020 that may be closed by this pull request
@davidBar-On davidBar-On force-pushed the iss873 branch 2 times, most recently from 635e879 to 6f8918e Compare September 18, 2021 10:30
@davidBar-On davidBar-On changed the title Add sendmmsg/recvmmsg support Add sendmmsg support for UDP Sep 18, 2021
@davidBar-On
Copy link
Contributor Author

@bmah888 I have change the PR to use -Z option for using sendmmsg for UDP, instead of adding a new option (I also rewrote the PR description). I also enhanced implementation, so if -Z is not used for UDP, practically there should not be any change to the current iperf3 behavior. Therefore the risk of adding these changes is low.

The UDP throughput enhancement achieved for high throughput interfaces is quite substantial, especially when the UDP messages are not large (witch is usually the case).

@database64128
Copy link

@bmah888 Any plan to have this PR reviewed and merged?

i = 0; /* count of messages sent */
r = 0; /* total bytes sent */
while (i < sp->sendmmsg_buffered_packets_count) {
j = sendmmsg(sp->socket, &sp->msg[i], sp->sendmmsg_buffered_packets_count - i, MSG_DONTWAIT);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before each sendmmsg(2) call, you should poll for socket write readiness. In my tests, this can significantly improve performance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add more details:

  1. How do you do the polling?
  2. From your experience, what should be done if the socket is not ready for write? The current design of the code is that the function does not return before all was sent (or there was an error). Do you suggest that in case the socket is not ready for write the function will return successfully, but without sending anything or before all was sent?
  3. Do you understand why the method you suggest improve performance? I am asking since in any case, iperf3 will retry sending.

Thanks

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you understand why the method you suggest improve performance?

In my case, I was doing raw syscalls in my Go program. The UDP socket opened by the Go runtime is in non-blocking mode. With sendmsg(2), if the sending operation was going to block, sendmsg(2) would return -EAGAIN or -EWOULDBLOCK, which is handled by the Go runtime to poll for socket write readiness with epoll. The calling goroutine can then be parked by the runtime to free the OS thread. (My limited understanding of Go internals might be inaccurate.)

Now with sendmmsg(2), according to the manual, a nonblocking call sends as many messages as possible (up to the limit specified by vlen) and returns immediately. By treating a non-complete return value the same way as -EAGAIN and -EWOULDBLOCK, that is, instead of immediately calling sendmmsg(2) again, I instructed the Go runtime to poll for write readiness before the next sendmmsg(2) call. This change yielded a 10% increase in throughput.

The current design of the code is that the function does not return before all was sent (or there was an error).

I'm not familiar with iperf3's code base. I just read some code, and it seems to me that iperf3 uses sockets in blocking mode for UDP tests. In this case, maybe it's better to simply drop the MSG_DONTWAIT flag, sendmmsg(2) would then only return when all messages have been sent. This saves even more syscall overhead.

Copy link
Contributor Author

@davidBar-On davidBar-On Apr 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@database64128, thanks a lot for the detailed explanation.

I will have to check how easy it is to implement this. To minimize iper3 design changes, the approach I took for sendmmsg is to accumulate packets iperf3 is sending and send them in bursts using sendmmsg. It may be that instead of the for loop, sendmmsg can be called once. In this case all the packets that were not sent can either be moved to the beginning of the buffer or ignored. (The issue with ignoring is that the packets are numbered, so the new packets numbering should start from the last successful packet sent.)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any update on this? Improved UDP send/recv performance on iperf would be very helpful!

@davidBar-On
Copy link
Contributor Author

I have rebased using version 3.20+. However, the previous version was pre-3.16 and did not use threads for sending/receiving the data. Now, since threads are used, the CPU is the limiting factor on my computer, and I get the same results for UDP with or without -Z. Tests should be run on a more powerful environment to see whether and how much using sendmmsg improves performance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UDP throughput issue

3 participants