perf: inline segment encode/decode to eliminate function call overhead

Replace chained ikcp_encode*/ikcp_decode* function calls with direct
binary.LittleEndian operations at fixed offsets in segment.encode()
and KCP.Input().

The original implementation used 6-7 sequential function calls that
each returned intermediate slices, adding unnecessary call overhead
and preventing compiler optimizations on the hot path.

Benchmark results (Apple M4 Pro, 6 samples, p=0.002):

  SegmentEncode:  3.192ns -> 2.895ns  (-9.32%)
  KCPInput:      12.11ns  -> 10.92ns  (-9.79%)

Zero change in allocations. All existing tests pass including race
detector.
This commit is contained in:
xtaci
2026-02-09 16:26:03 +08:00
parent 56b1fffecd
commit c8a3a26370
+18 -17
View File
@@ -180,16 +180,16 @@ type segment struct {
// encode a segment into buffer
func (seg *segment) encode(ptr []byte) []byte {
ptr = ikcp_encode32u(ptr, seg.conv)
ptr = ikcp_encode8u(ptr, seg.cmd)
ptr = ikcp_encode8u(ptr, seg.frg)
ptr = ikcp_encode16u(ptr, seg.wnd)
ptr = ikcp_encode32u(ptr, seg.ts)
ptr = ikcp_encode32u(ptr, seg.sn)
ptr = ikcp_encode32u(ptr, seg.una)
ptr = ikcp_encode32u(ptr, uint32(len(seg.data)))
binary.LittleEndian.PutUint32(ptr, seg.conv)
ptr[4] = seg.cmd
ptr[5] = seg.frg
binary.LittleEndian.PutUint16(ptr[6:], seg.wnd)
binary.LittleEndian.PutUint32(ptr[8:], seg.ts)
binary.LittleEndian.PutUint32(ptr[12:], seg.sn)
binary.LittleEndian.PutUint32(ptr[16:], seg.una)
binary.LittleEndian.PutUint32(ptr[20:], uint32(len(seg.data)))
atomic.AddUint64(&DefaultSnmp.OutSegs, 1)
return ptr
return ptr[IKCP_OVERHEAD:]
}
// segmentHeap is a min-heap of segments, used for receiving segments in order
@@ -617,18 +617,19 @@ func (kcp *KCP) Input(data []byte, pktType PacketType, ackNoDelay bool) int {
break
}
data = ikcp_decode32u(data, &conv)
conv = binary.LittleEndian.Uint32(data)
if conv != kcp.conv {
return -1
}
data = ikcp_decode8u(data, &cmd)
data = ikcp_decode8u(data, &frg)
data = ikcp_decode16u(data, &wnd)
data = ikcp_decode32u(data, &ts)
data = ikcp_decode32u(data, &sn)
data = ikcp_decode32u(data, &una)
data = ikcp_decode32u(data, &length)
cmd = data[4]
frg = data[5]
wnd = binary.LittleEndian.Uint16(data[6:])
ts = binary.LittleEndian.Uint32(data[8:])
sn = binary.LittleEndian.Uint32(data[12:])
una = binary.LittleEndian.Uint32(data[16:])
length = binary.LittleEndian.Uint32(data[20:])
data = data[IKCP_OVERHEAD:]
kcp.debugLog(IKCP_LOG_INPUT, "conv", conv, "cmd", cmd, "frg", frg, "wnd", wnd, "ts", ts, "sn", sn, "una", una, "len", length, "datalen", len(data))