-
Notifications
You must be signed in to change notification settings - Fork 446
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Don't delay ACKs for significant window updates #935
Conversation
This seems like a small, well explained and important PR, I think all dependent projects would benefit Overall, impressive performance boost! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks OK to me other but I'd like to see some more comments.
| State::FinWait2 => { | ||
let new_win = self.scaled_window(); | ||
if let Some(last_win) = self.last_scaled_window() { | ||
new_win > 0 && new_win / 2 >= last_win |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment here explaining the calculation (the definition of "significantly" from the PR body)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. I've added as the doc comments, see https://github.com/smoltcp-rs/smoltcp/compare/ca9749d3e8c8a075568e0506ccc008d63cf7ea0f..fe179c918415d6c251384b24fdd6204060265431.
Also, can you update the benchmark results in the README please? |
Thanks to @tomDev5 and @whitequark for your comments and review!
I'll add it as
Do you mean the results of The benchmark itself needs some fixesSince the TCP buffer is a ring buffer, the following Lines 142 to 152 in c937695
This is problematic because after receiving the trailing part of the ring buffer in this I believe the intention here is to receive all the data, like: while socket.can_recv() && processed < AMOUNT {
let length = socket
.recv(|buffer| {
let length = cmp::min(buffer.len(), AMOUNT - processed);
(length, length)
})
.unwrap();
processed += length;
} After I made this change, the throughput of The benchmark itself is not related to this PRThe MTU of Lines 2049 to 2055 in c937695
If the ACK timer is not even started, the delayed_ack_expired method will always return true , so all subsequent window updates (not necessarily significant ones) will not be delayed, as shown in the following code:Lines 2240 to 2242 in c937695
ConclusionGiven the above analysis, I think I need to open a separate PR to fix the benchmarks and update the results instead of doing it in this PR. (EDIT: See #952.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
Hi smoltcp maintainers, thanks for your great work!
I've been experiencing extremely low performance (< 1Gbps) for two TCP sockets connected via the loopback device (PoC attached at the end of this PR). Further investigation reveals that ACKs carrying significant window updates are being delayed due to the delayed ACK mechanism. Although disabling the ACK delay serves as a workaround, I think it's better to go ahead and fix the bug here, so I'm submitting this PR.
According to the Linux kernel implementation, ACKs are always sent immediately when the receive window is significantly increased. Here "significantly" means doubling the receive window. However, this logic does not seem to be implemented by smoltcp, where no ACKs will be sent unless the delay ACK timer expires:
smoltcp/src/socket/tcp.rs
Lines 2205 to 2207 in ef67e7b
Another Problem
When I take a look at the
window_to_update
function, I also notice another problem: In the following code snippet,self.remote_last_win
is the length of the last receive window, which cannot be directly compared with the length of the current receive window, because the start positions of the two windows are different.smoltcp/src/socket/tcp.rs
Lines 2133 to 2142 in ef67e7b
For example, suppose the sender has sent packets that fill all the slots in the receive window, which means the sender cannot send any more packets unless the receiver responds with a window update. To be aware of this fact, the effective length of the last receive window should be treated as zero instead of the previously advertised length.
I've added a new function called
last_scaled_window
to fix the second problem, which should work in the same way as thetcp_receive_window
function found in the Linux kernel implementation.PoC
The PoC is modified from
examples/loopback.rs
:Without this fix:
With this fix: