Skip to content

Commit 0225465

Browse files
AlexanderYastrebovjwhited
authored andcommitted
device: optimize message encoding
Optimize message encoding by eliminating binary.Write (which internally uses reflection) in favour of hand-rolled encoding. This is companion to 9e7529c. Synthetic benchmark: var packetSink []byte func BenchmarkMessageInitiationMarshal(b *testing.B) { var msg MessageInitiation b.Run("binary.Write", func(b *testing.B) { b.ReportAllocs() for range b.N { var buf [MessageInitiationSize]byte writer := bytes.NewBuffer(buf[:0]) _ = binary.Write(writer, binary.LittleEndian, msg) packetSink = writer.Bytes() } }) b.Run("binary.Encode", func(b *testing.B) { b.ReportAllocs() for range b.N { packet := make([]byte, MessageInitiationSize) _, _ = binary.Encode(packet, binary.LittleEndian, msg) packetSink = packet } }) b.Run("marshal", func(b *testing.B) { b.ReportAllocs() for range b.N { packet := make([]byte, MessageInitiationSize) _ = msg.marshal(packet) packetSink = packet } }) } Results: │ - │ │ sec/op │ MessageInitiationMarshal/binary.Write-8 1.337µ ± 0% MessageInitiationMarshal/binary.Encode-8 1.242µ ± 0% MessageInitiationMarshal/marshal-8 53.05n ± 1% │ - │ │ B/op │ MessageInitiationMarshal/binary.Write-8 368.0 ± 0% MessageInitiationMarshal/binary.Encode-8 160.0 ± 0% MessageInitiationMarshal/marshal-8 160.0 ± 0% │ - │ │ allocs/op │ MessageInitiationMarshal/binary.Write-8 3.000 ± 0% MessageInitiationMarshal/binary.Encode-8 1.000 ± 0% MessageInitiationMarshal/marshal-8 1.000 ± 0% cherry picked from commit WireGuard/wireguard-go@264889f Updates tailscale/corp#28879 Signed-off-by: Alexander Yastrebov <[email protected]> Signed-off-by: Jason A. Donenfeld <[email protected]>
1 parent ae06362 commit 0225465

File tree

2 files changed

+53
-13
lines changed

2 files changed

+53
-13
lines changed

device/noise-protocol.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,22 @@ func (msg *MessageInitiation) unmarshal(b []byte) error {
134134
return nil
135135
}
136136

137+
func (msg *MessageInitiation) marshal(b []byte) error {
138+
if len(b) != MessageInitiationSize {
139+
return errMessageLengthMismatch
140+
}
141+
142+
binary.LittleEndian.PutUint32(b, msg.Type)
143+
binary.LittleEndian.PutUint32(b[4:], msg.Sender)
144+
copy(b[8:], msg.Ephemeral[:])
145+
copy(b[8+len(msg.Ephemeral):], msg.Static[:])
146+
copy(b[8+len(msg.Ephemeral)+len(msg.Static):], msg.Timestamp[:])
147+
copy(b[8+len(msg.Ephemeral)+len(msg.Static)+len(msg.Timestamp):], msg.MAC1[:])
148+
copy(b[8+len(msg.Ephemeral)+len(msg.Static)+len(msg.Timestamp)+len(msg.MAC1):], msg.MAC2[:])
149+
150+
return nil
151+
}
152+
137153
func (msg *MessageResponse) unmarshal(b []byte) error {
138154
if len(b) != MessageResponseSize {
139155
return errMessageLengthMismatch
@@ -150,6 +166,22 @@ func (msg *MessageResponse) unmarshal(b []byte) error {
150166
return nil
151167
}
152168

169+
func (msg *MessageResponse) marshal(b []byte) error {
170+
if len(b) != MessageResponseSize {
171+
return errMessageLengthMismatch
172+
}
173+
174+
binary.LittleEndian.PutUint32(b, msg.Type)
175+
binary.LittleEndian.PutUint32(b[4:], msg.Sender)
176+
binary.LittleEndian.PutUint32(b[8:], msg.Receiver)
177+
copy(b[12:], msg.Ephemeral[:])
178+
copy(b[12+len(msg.Ephemeral):], msg.Empty[:])
179+
copy(b[12+len(msg.Ephemeral)+len(msg.Empty):], msg.MAC1[:])
180+
copy(b[12+len(msg.Ephemeral)+len(msg.Empty)+len(msg.MAC1):], msg.MAC2[:])
181+
182+
return nil
183+
}
184+
153185
func (msg *MessageCookieReply) unmarshal(b []byte) error {
154186
if len(b) != MessageCookieReplySize {
155187
return errMessageLengthMismatch
@@ -163,6 +195,19 @@ func (msg *MessageCookieReply) unmarshal(b []byte) error {
163195
return nil
164196
}
165197

198+
func (msg *MessageCookieReply) marshal(b []byte) error {
199+
if len(b) != MessageCookieReplySize {
200+
return errMessageLengthMismatch
201+
}
202+
203+
binary.LittleEndian.PutUint32(b, msg.Type)
204+
binary.LittleEndian.PutUint32(b[4:], msg.Receiver)
205+
copy(b[8:], msg.Nonce[:])
206+
copy(b[8+len(msg.Nonce):], msg.Cookie[:])
207+
208+
return nil
209+
}
210+
166211
type Handshake struct {
167212
state handshakeState
168213
mutex sync.RWMutex

device/send.go

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
package device
77

88
import (
9-
"bytes"
109
"encoding/binary"
1110
"errors"
1211
"net"
@@ -124,10 +123,8 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
124123
return err
125124
}
126125

127-
var buf [MessageInitiationSize]byte
128-
writer := bytes.NewBuffer(buf[:0])
129-
binary.Write(writer, binary.LittleEndian, msg)
130-
packet := writer.Bytes()
126+
packet := make([]byte, MessageInitiationSize)
127+
_ = msg.marshal(packet)
131128
peer.cookieGenerator.AddMacs(packet)
132129

133130
peer.timersAnyAuthenticatedPacketTraversal()
@@ -155,10 +152,8 @@ func (peer *Peer) SendHandshakeResponse() error {
155152
return err
156153
}
157154

158-
var buf [MessageResponseSize]byte
159-
writer := bytes.NewBuffer(buf[:0])
160-
binary.Write(writer, binary.LittleEndian, response)
161-
packet := writer.Bytes()
155+
packet := make([]byte, MessageResponseSize)
156+
_ = response.marshal(packet)
162157
peer.cookieGenerator.AddMacs(packet)
163158

164159
err = peer.BeginSymmetricSession()
@@ -189,11 +184,11 @@ func (device *Device) SendHandshakeCookie(initiatingElem *QueueHandshakeElement)
189184
return err
190185
}
191186

192-
var buf [MessageCookieReplySize]byte
193-
writer := bytes.NewBuffer(buf[:0])
194-
binary.Write(writer, binary.LittleEndian, reply)
187+
packet := make([]byte, MessageCookieReplySize)
188+
_ = reply.marshal(packet)
195189
// TODO: allocation could be avoided
196-
device.net.bind.Send([][]byte{writer.Bytes()}, initiatingElem.endpoint)
190+
device.net.bind.Send([][]byte{packet}, initiatingElem.endpoint)
191+
197192
return nil
198193
}
199194

0 commit comments

Comments
 (0)