This is important for correctness, because the current incremental checksum
update routine does not work correctly when the field is set to the same value.
See this equation from RFC 1071:
C - one's complement sum of old header
C' - one's complement sum of new header
m - old value of a 16-bit field
m' - new value of a 16-bit field
C' = C + (-m) + m' = C + (m' - m)
Assuming m is a 16-bit field, when m == m', then (m' - m) == (m + ~m) will
always equal -0, or 0xFFFF in one's complement arithmetic, rather than 0x0000.
The additive identity is typically 0, but since -0 has a different
representation, it is *not* an identity and therefore the equation produces a
different value.
PiperOrigin-RevId: 610963455
Adding extra padding to this value seems to improve performance on gVNIC
hardware. When TCPTotalHeaderMaximumSize is <140, 10MB PUT commands start
randomly taking 10x as long as they normally do.
Fixes#9816
PiperOrigin-RevId: 598024702
It is an idea of running codespell as part of our presubmit checks.
Before enabling it for new changes, let's fix what it has found.
Signed-off-by: Andrei Vagin <avagin@gmail.com>
This is a roll-forward of cl/540620826. The differences are:
- Added Tony's test that showed breakage on ARM64
- Fixed the 64-bit non-AMD64 checksum implementation
- Handles odd buffers correctly
- Doesn't call the unrolled checksum impl anymore
- Re-ordered calculateChecksum to reduce indentation
Otherwise this is the same CL. The only changes are in checksum_test.go and
checksum_noasm_unsafe.go.
PiperOrigin-RevId: 547890206
tcp_benchmark shows we spend significant time checksumming -- after Syscall,
checksumming is the most time-consuming function.
AMD64 gets a 77% faster checksum via assmebly implementation.
Other 64bit machines get a new checksum function, which runs 38% faster than
current checksumming on my machine and 34% faster on an ARM test machine.
tcp_benchmark is pretty noisy here, but generally shows a 2-5% decrease in CPU
usage or a small boost to throughput:
```
│ /tmp/old.log │ /tmp/new.log │
│ Mb/s │ Mb/s vs base │
TCP/role=server/host-gso=false/host-gro=false 142.0 ± 28% 154.5 ± 24% ~ (p=0.459 n=40)
TCP/role=client/host-gso=false/host-gro=false 1.794k ± 1% 1.840k ± 1% +2.59% (p=0.000 n=40)
geomean 504.7 533.2 +5.65%
│ /tmp/old.log │ /tmp/new.log │
│ cpu-time │ cpu-time vs base │
TCP/role=server/host-gso=false/host-gro=false 244.0m ± 25% 256.3m ± 21% ~ (p=0.529 n=40)
TCP/role=client/host-gso=false/host-gro=false 2.251 ± 1% 2.242 ± 1% ~ (p=0.079 n=40)
geomean 741.1m 758.1m +2.30%
```
So it seems checksumming is not the bottleneck in tcp_benchmark. This may be
different in other environments, especially those where we cannot rely on host
receive checksum offload.
PiperOrigin-RevId: 540620826
tcp_benchmark throughput increase 2-3%, but allocations go down (25% in the
download benchmark, only 2% in the upload path).
PiperOrigin-RevId: 532523146
The important change here is in tcpip/tcpip.go, where tcpip.Address is defined.
The rest is updating uses of tcpip.Address.
This is preparation for netip.Addr or []byte based addresses, which should save
us a bunch of allocations. Currently, we allocate every time we want to, say,
get a tcpip.Address from a header. This is because the header is a byte slice,
but Address is a string. Strings are immutable, so Go allocates and copies.
PiperOrigin-RevId: 532284732
Opted for separate code paths rather than over-fitting to interfaces when
there're only 2 supported network protocols and 1 transport protocol.
The performance improvement is more modest than IPv4, I'm guessing due to the
process of parsing extension headers. As with IPv4, we'd benefit from parsing
only once and saving header state.
PiperOrigin-RevId: 513928270