From c8a3a263704e875a3148b747cd0ecdc2da51bd0e Mon Sep 17 00:00:00 2001 From: xtaci Date: Mon, 9 Feb 2026 16:26:03 +0800 Subject: [PATCH] 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. --- kcp.go | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/kcp.go b/kcp.go index eb4271b..d809157 100644 --- a/kcp.go +++ b/kcp.go @@ -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))