Skip to contentSkip to conversation

TCP small packets throughput and multiqueue virtio-net

PATCH Jason Wang 4 participants 6 messages Mar 8, 2013 – Mar 11, 2013

Subsystems (4)
lore
Share & tools
lore

Jason Wang

author
#0 lore raw

Hello all:

I meet an issue when testing multiqueue virtio-net. When I testing guest small packets stream sending performance with netperf. I find an regression of multiqueue. When I run 2 sessions of TCP_STREAM test with 1024 byte from guest to local host, I get following result:

1q result: 3457.64
2q result: 7781.45

Statistics shows that: compared with one queue, multiqueue tends to send much more but smaller packets. Tcpdump shows single queue has a much higher possibility to produce a 64K gso packet compared to multiqueue. More but smaller packets will cause more vmexits and interrupts which lead a degradation on throughput.

Then problem only exist for small packets sending. When I test with larger size, multiqueue will gradually outperfrom single queue. And multiqueue also outperfrom in both TCP_RR and pktgen test (even with small packets). The problem disappear when I turn off both gso and tso.

I'm not sure whether it's a bug or expected since anyway we get improvement on latency. And if it's a bug, I suspect it was related to TCP GSO batching algorithm who tends to batch less in this situation. ( Jiri Pirko suspect it was the defect of virtio-net driver, but I didn't find any obvious clue on this). After some experiments, I find the it maybe related to tcp_tso_should_defer(), after
1) change the tcp_tso_win_divisor to 1
2) the following changes
the throughput were almost the same (but still a little worse) as single queue:

net/ipv4/tcp_output.c +0 -0
index fd0cea1..dedd2a6 100644
@@ -1777,10 +1777,12 @@ static bool tcp_tso_should_defer(struct sock

*sk, struct sk_buff *skb)

        limit = min(send_win, cong_win);

+#if 0

        /* If a full-sized TSO skb can be sent, do it. */
        if (limit >= min_t(unsigned int, sk->sk_gso_max_size,
                           sk->sk_gso_max_segs * tp->mss_cache))
                goto send_now;

+#endif

        /* Middle in queue won't get any more data, full sendable
already? */
        if ((skb != tcp_write_queue_tail(sk)) && (limit >= skb->len))

Git history shows this check were added for both westwood and fasttcp, I'm not familiar with tcp but looks like we can easily hit this check under when multiqueue is enabled for virtio-net. Maybe I was wrong but I wonder whether we can still do some batching here.

Any comments, thoughts are welcomed.

Thanks

#1 lore raw
Quoted 16 lines · Jason Wang

Hello all:

I meet an issue when testing multiqueue virtio-net. When I testing guest small packets stream sending performance with netperf. I find an regression of multiqueue. When I run 2 sessions of TCP_STREAM test with 1024 byte from guest to local host, I get following result:

1q result: 3457.64
2q result: 7781.45

Statistics shows that: compared with one queue, multiqueue tends to send much more but smaller packets. Tcpdump shows single queue has a much higher possibility to produce a 64K gso packet compared to multiqueue. More but smaller packets will cause more vmexits and interrupts which lead a degradation on throughput.

Quoted 5 lines

Then problem only exist for small packets sending. When I test with larger size, multiqueue will gradually outperfrom single queue. And multiqueue also outperfrom in both TCP_RR and pktgen test (even with small packets). The problem disappear when I turn off both gso and tso.

This makes little sense to me : TCP_RR workload (assuming one byte payload) cannot use GSO or TSO anyway. Same for pktgen as it uses UDP.

Quoted 29 lines

I'm not sure whether it's a bug or expected since anyway we get improvement on latency. And if it's a bug, I suspect it was related to TCP GSO batching algorithm who tends to batch less in this situation. ( Jiri Pirko suspect it was the defect of virtio-net driver, but I didn't find any obvious clue on this). After some experiments, I find the it maybe related to tcp_tso_should_defer(), after
1) change the tcp_tso_win_divisor to 1
2) the following changes
the throughput were almost the same (but still a little worse) as single queue:

net/ipv4/tcp_output.c +2 -0
index fd0cea1..dedd2a6 100644
@@ -1777,10 +1777,12 @@ static bool tcp_tso_should_defer(struct sock
 *sk, struct sk_buff *skb)
 
        limit = min(send_win, cong_win);
 
+#if 0
        /* If a full-sized TSO skb can be sent, do it. */
        if (limit >= min_t(unsigned int, sk->sk_gso_max_size,
                           sk->sk_gso_max_segs * tp->mss_cache))
                goto send_now;
+#endif
 
        /* Middle in queue won't get any more data, full sendable

already? */

Quoted 9 lines
        if ((skb != tcp_write_queue_tail(sk)) && (limit >= skb->len))

Git history shows this check were added for both westwood and fasttcp, I'm not familiar with tcp but looks like we can easily hit this check under when multiqueue is enabled for virtio-net. Maybe I was wrong but I wonder whether we can still do some batching here.

Any comments, thoughts are welcomed.

Well, the point is : if your app does write(1024) bytes, thats probably because it wants small packets from the very beginning. (See the TCP PUSH flag ?)

If the transport is slow, TCP stack will automatically collapse several write into single skbs (assuming TSO or GSO is on), and you'll see big GSO packets with tcpdump [1]. So TCP will help you to get less overhead in this case.

But if transport is fast, you'll see small packets, and thats good for latency.

So my opinion is : its exactly behaving as expected.

If you want bigger packets either :
- Make the application doing big write()
- Slow the vmexit ;)

[1] GSO fools tcpdump : Actual packets sent to the wire are not 'big packets', but they hit dev_hard_start_xmit() as GSO packets.

#2 lore raw
Quoted 5 lines

Well, the point is : if your app does write(1024) bytes, thats probably because it wants small packets from the very beginning. (See the TCP PUSH flag ?)

I think that raises the question of whether or not Jason was setting the test-specific -D option on his TCP_STREAM tests, to have netperf make a setsockopt(TCP_NODELAY) call.

happy benchmarking,

rick jones

Quoted 24 lines

If the transport is slow, TCP stack will automatically collapse several write into single skbs (assuming TSO or GSO is on), and you'll see big GSO packets with tcpdump [1]. So TCP will help you to get less overhead in this case.

But if transport is fast, you'll see small packets, and thats good for latency.

So my opinion is : its exactly behaving as expected.

If you want bigger packets either :
- Make the application doing big write()
- Slow the vmexit ;)

[1] GSO fools tcpdump : Actual packets sent to the wire are not 'big packets', but they hit dev_hard_start_xmit() as GSO packets.

--
To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

author
#3 lore raw
Quoted 9 lines · Rick Jones
Quoted 5 lines

Well, the point is : if your app does write(1024) bytes, thats probably because it wants small packets from the very beginning. (See the TCP PUSH flag ?)

I think that raises the question of whether or not Jason was setting the test-specific -D option on his TCP_STREAM tests, to have netperf make a setsockopt(TCP_NODELAY) call.

I didn't set the -D option to disable nagle. But I get almost the almost same result with -D (1024 byte TCP_STREAM from guest to local host).

Quoted 30 lines

happy benchmarking,

rick jones

Quoted 24 lines

If the transport is slow, TCP stack will automatically collapse several write into single skbs (assuming TSO or GSO is on), and you'll see big GSO packets with tcpdump [1]. So TCP will help you to get less overhead in this case.

But if transport is fast, you'll see small packets, and thats good for latency.

So my opinion is : its exactly behaving as expected.

If you want bigger packets either :
- Make the application doing big write()
- Slow the vmexit ;)

[1] GSO fools tcpdump : Actual packets sent to the wire are not 'big packets', but they hit dev_hard_start_xmit() as GSO packets.

Signature · 3 lines
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
author
#4 lore raw
In reply to Eric Dumazet
Quoted 61 lines · Eric Dumazet
Quoted 16 lines · Jason Wang

Hello all:

I meet an issue when testing multiqueue virtio-net. When I testing guest small packets stream sending performance with netperf. I find an regression of multiqueue. When I run 2 sessions of TCP_STREAM test with 1024 byte from guest to local host, I get following result:

1q result: 3457.64
2q result: 7781.45

Statistics shows that: compared with one queue, multiqueue tends to send much more but smaller packets. Tcpdump shows single queue has a much higher possibility to produce a 64K gso packet compared to multiqueue. More but smaller packets will cause more vmexits and interrupts which lead a degradation on throughput.

Quoted 5 lines

Then problem only exist for small packets sending. When I test with larger size, multiqueue will gradually outperfrom single queue. And multiqueue also outperfrom in both TCP_RR and pktgen test (even with small packets). The problem disappear when I turn off both gso and tso.

This makes little sense to me : TCP_RR workload (assuming one byte payload) cannot use GSO or TSO anyway. Same for pktgen as it uses UDP.

Quoted 36 lines

I'm not sure whether it's a bug or expected since anyway we get improvement on latency. And if it's a bug, I suspect it was related to TCP GSO batching algorithm who tends to batch less in this situation. ( Jiri Pirko suspect it was the defect of virtio-net driver, but I didn't find any obvious clue on this). After some experiments, I find the it maybe related to tcp_tso_should_defer(), after
1) change the tcp_tso_win_divisor to 1
2) the following changes
the throughput were almost the same (but still a little worse) as single queue:

net/ipv4/tcp_output.c +2 -0
index fd0cea1..dedd2a6 100644
@@ -1777,10 +1777,12 @@ static bool tcp_tso_should_defer(struct sock
 *sk, struct sk_buff *skb)
 
        limit = min(send_win, cong_win);
 
+#if 0
        /* If a full-sized TSO skb can be sent, do it. */
        if (limit >= min_t(unsigned int, sk->sk_gso_max_size,
                           sk->sk_gso_max_segs * tp->mss_cache))
                goto send_now;
+#endif
 
        /* Middle in queue won't get any more data, full sendable

already? */

        if ((skb != tcp_write_queue_tail(sk)) && (limit >= skb->len))

Git history shows this check were added for both westwood and fasttcp, I'm not familiar with tcp but looks like we can easily hit this check under when multiqueue is enabled for virtio-net. Maybe I was wrong but I wonder whether we can still do some batching here.

Quoted 5 lines
Quoted 2 lines

Any comments, thoughts are welcomed.

Well, the point is : if your app does write(1024) bytes, thats probably because it wants small packets from the very beginning. (See the TCP PUSH flag ?)

Didn't fully understand the question, but according to the tcpdump, TCP PUSH flag were seen in very few packets.

Quoted 5 lines

If the transport is slow, TCP stack will automatically collapse several write into single skbs (assuming TSO or GSO is on), and you'll see big GSO packets with tcpdump [1]. So TCP will help you to get less overhead in this case.

Quoted 8 lines

But if transport is fast, you'll see small packets, and thats good for latency.

So my opinion is : its exactly behaving as expected.

If you want bigger packets either :
- Make the application doing big write()
- Slow the vmexit ;)

Good to know this, thanks for the explanation.

Quoted 5 lines

[1] GSO fools tcpdump : Actual packets sent to the wire are not 'big packets', but they hit dev_hard_start_xmit() as GSO packets.

#5 lore raw
In reply to Eric Dumazet
Quoted 53 lines · Eric Dumazet
Quoted 16 lines · Jason Wang

Hello all:

I meet an issue when testing multiqueue virtio-net. When I testing guest small packets stream sending performance with netperf. I find an regression of multiqueue. When I run 2 sessions of TCP_STREAM test with 1024 byte from guest to local host, I get following result:

1q result: 3457.64
2q result: 7781.45

Statistics shows that: compared with one queue, multiqueue tends to send much more but smaller packets. Tcpdump shows single queue has a much higher possibility to produce a 64K gso packet compared to multiqueue. More but smaller packets will cause more vmexits and interrupts which lead a degradation on throughput.

Quoted 5 lines

Then problem only exist for small packets sending. When I test with larger size, multiqueue will gradually outperfrom single queue. And multiqueue also outperfrom in both TCP_RR and pktgen test (even with small packets). The problem disappear when I turn off both gso and tso.

This makes little sense to me : TCP_RR workload (assuming one byte payload) cannot use GSO or TSO anyway. Same for pktgen as it uses UDP.

Quoted 27 lines

I'm not sure whether it's a bug or expected since anyway we get improvement on latency. And if it's a bug, I suspect it was related to TCP GSO batching algorithm who tends to batch less in this situation. ( Jiri Pirko suspect it was the defect of virtio-net driver, but I didn't find any obvious clue on this). After some experiments, I find the it maybe related to tcp_tso_should_defer(), after
1) change the tcp_tso_win_divisor to 1
2) the following changes
the throughput were almost the same (but still a little worse) as single queue:

net/ipv4/tcp_output.c +2 -0
index fd0cea1..dedd2a6 100644
@@ -1777,10 +1777,12 @@ static bool tcp_tso_should_defer(struct sock
 *sk, struct sk_buff *skb)
 
        limit = min(send_win, cong_win);
 
+#if 0
        /* If a full-sized TSO skb can be sent, do it. */
        if (limit >= min_t(unsigned int, sk->sk_gso_max_size,
                           sk->sk_gso_max_segs * tp->mss_cache))
                goto send_now;
+#endif
 
Quoted 5 lines
Quoted 5 lines
net/ipv4/tcp_output.c +0 -0
        /* Middle in queue won't get any more data, full sendable

already? */

        if ((skb != tcp_write_queue_tail(sk)) && (limit >= skb->len))

Git history shows this check were added for both westwood and fasttcp,

Hmm yes,
http://www.mail-archive.com/[email protected]/msg08738.html
How about we disable it for cubic,reno then?

Quoted 10 lines
Quoted 6 lines

I'm not familiar with tcp but looks like we can easily hit this check under when multiqueue is enabled for virtio-net. Maybe I was wrong but I wonder whether we can still do some batching here.

Any comments, thoughts are welcomed.

Well, the point is : if your app does write(1024) bytes, thats probably because it wants small packets from the very beginning. (See the TCP PUSH flag ?)

In that case the application typically won't have packets in flight (e.g. like TCP_RR) so TSO won't trigger at all, no?

It would seem that packets in flight might rather indicate that the application is trying to keep the socket buffer full by giving the kernel data as fast as it becomes available. At least, this is exactly what benchmark tools seem to be doing, right?

Quoted 7 lines

If the transport is slow, TCP stack will automatically collapse several write into single skbs (assuming TSO or GSO is on), and you'll see big GSO packets with tcpdump [1]. So TCP will help you to get less overhead in this case.

But if transport is fast, you'll see small packets, and thats good for latency.

But is a large CWND really a good indicator of a low latency link? Can't CWND grow (depending on the protocol) as long as there's no packet loss even if the latency is high?

So if a VM is using a 10G backend link in the host, it seems that (due to vmexit overhead) the latency is high so we are not gaining much from sending the packet a bit earlier, OTOH, the bandwidth is high so the per packet overhead becomes measureable. Virt workloads are probably not the only ones that are like that, it's just that it's easier for people to observe the overhead.

Quoted 9 lines

So my opinion is : its exactly behaving as expected.

If you want bigger packets either :
- Make the application doing big write()
- Slow the vmexit ;)

[1] GSO fools tcpdump : Actual packets sent to the wire are not 'big packets', but they hit dev_hard_start_xmit() as GSO packets.

Signature · 1 line
MST