Skip to content
This repository was archived by the owner on May 26, 2022. It is now read-only.

Commit 02dc2ad

Browse files
Merge pull request #81 from libp2p/feat/77
Further Optimizations and refactor benchmarking code.
2 parents 69090b2 + 9335a6e commit 02dc2ad

File tree

8 files changed

+309
-70
lines changed

8 files changed

+309
-70
lines changed

benchmark_test.go

Lines changed: 90 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,36 @@ package noise
22

33
import (
44
"context"
5-
"github.com/libp2p/go-libp2p-core/crypto"
6-
"github.com/libp2p/go-libp2p-core/sec"
5+
"golang.org/x/crypto/poly1305"
76
"io"
87
"io/ioutil"
98
"math/rand"
109
"net"
1110
"testing"
1211
"time"
12+
13+
"github.com/libp2p/go-libp2p-core/crypto"
14+
"github.com/libp2p/go-libp2p-core/sec"
15+
)
16+
17+
type testMode int
18+
19+
const (
20+
readBufferGtEncMsg testMode = iota
21+
readBufferLtPlainText
1322
)
1423

24+
var bcs = map[string]struct {
25+
m testMode
26+
}{
27+
"readBuffer > encrypted message": {
28+
readBufferGtEncMsg,
29+
},
30+
"readBuffer < decrypted plaintext": {
31+
readBufferLtPlainText,
32+
},
33+
}
34+
1535
func makeTransport(b *testing.B) *Transport {
1636
b.Helper()
1737

@@ -78,28 +98,48 @@ func (b benchenv) connect(stopTimer bool) (*secureSession, *secureSession) {
7898
return initSession.(*secureSession), respSession.(*secureSession)
7999
}
80100

81-
func drain(r io.Reader, done chan<- error) {
82-
_, err := io.Copy(ioutil.Discard, r)
101+
func drain(r io.Reader, done chan<- error, writeTo io.Writer) {
102+
_, err := io.Copy(writeTo, r)
83103
done <- err
84104
}
85105

86-
func sink(dst io.WriteCloser, src io.Reader, done chan<- error) {
87-
_, err := io.Copy(dst, src)
106+
type discardWithBuffer struct {
107+
buf []byte
108+
io.Writer
109+
}
110+
111+
func (d *discardWithBuffer) ReadFrom(r io.Reader) (n int64, err error) {
112+
readSize := 0
113+
for {
114+
readSize, err = r.Read(d.buf)
115+
n += int64(readSize)
116+
if err != nil {
117+
if err == io.EOF {
118+
return n, nil
119+
}
120+
return
121+
}
122+
}
123+
}
124+
125+
func sink(dst io.WriteCloser, src io.Reader, done chan<- error, buf []byte) {
126+
_, err := io.CopyBuffer(dst, src, buf)
88127
if err != nil {
89128
done <- err
90129
}
91130
done <- dst.Close()
92131
}
93132

94-
func pipeRandom(src rand.Source, w io.WriteCloser, r io.Reader, n int64) error {
133+
func pipeRandom(src rand.Source, w io.WriteCloser, r io.Reader, n int64, plainTextBuf []byte,
134+
writeTo io.Writer) error {
95135
rnd := rand.New(src)
96136
lr := io.LimitReader(rnd, n)
97137

98138
writeCh := make(chan error, 1)
99139
readCh := make(chan error, 1)
100140

101-
go sink(w, lr, writeCh)
102-
go drain(r, readCh)
141+
go sink(w, lr, writeCh, plainTextBuf)
142+
go drain(r, readCh, writeTo)
103143

104144
writeDone := false
105145
readDone := false
@@ -121,39 +161,74 @@ func pipeRandom(src rand.Source, w io.WriteCloser, r io.Reader, n int64) error {
121161
return nil
122162
}
123163

124-
func benchDataTransfer(b *benchenv, size int64) {
164+
func benchDataTransfer(b *benchenv, dataSize int64, m testMode) {
125165
var totalBytes int64
126166
var totalTime time.Duration
127167

168+
plainTextBufs := make([][]byte, 61)
169+
writeTos := make(map[int]io.Writer)
170+
for i := 0; i < len(plainTextBufs); i++ {
171+
var rbuf []byte
172+
// plaintext will be 2 KB to 62 KB
173+
plainTextBufs[i] = make([]byte, (i+2)*1024)
174+
switch m {
175+
case readBufferGtEncMsg:
176+
rbuf = make([]byte, len(plainTextBufs[i])+poly1305.TagSize+1)
177+
case readBufferLtPlainText:
178+
rbuf = make([]byte, len(plainTextBufs[i])-2)
179+
}
180+
writeTos[i] = &discardWithBuffer{rbuf, ioutil.Discard}
181+
}
182+
128183
b.ResetTimer()
129184
b.ReportAllocs()
130185

131186
for i := 0; i < b.N; i++ {
132187
initSession, respSession := b.connect(true)
133188

134189
start := time.Now()
135-
err := pipeRandom(b.rndSrc, initSession, respSession, size)
190+
191+
bufi := i % len(plainTextBufs)
192+
err := pipeRandom(b.rndSrc, initSession, respSession, dataSize, plainTextBufs[bufi], writeTos[bufi])
136193
if err != nil {
137194
b.Fatalf("error sending random data: %s", err)
138195
}
139196
elapsed := time.Since(start)
140197
totalTime += elapsed
141-
totalBytes += size
198+
totalBytes += dataSize
142199
}
143200
bytesPerSec := float64(totalBytes) / totalTime.Seconds()
144201
b.ReportMetric(bytesPerSec, "bytes/sec")
145202
}
146203

204+
type bc struct {
205+
plainTextChunkLen int64
206+
readBufferLen int64
207+
}
208+
147209
func BenchmarkTransfer1MB(b *testing.B) {
148-
benchDataTransfer(setupEnv(b), 1024*1024)
210+
for n, bc := range bcs {
211+
b.Run(n, func(b *testing.B) {
212+
benchDataTransfer(setupEnv(b), 1024*1024, bc.m)
213+
})
214+
}
215+
149216
}
150217

151218
func BenchmarkTransfer100MB(b *testing.B) {
152-
benchDataTransfer(setupEnv(b), 1024*1024*100)
219+
for n, bc := range bcs {
220+
b.Run(n, func(b *testing.B) {
221+
benchDataTransfer(setupEnv(b), 1024*1024*100, bc.m)
222+
})
223+
}
153224
}
154225

155226
func BenchmarkTransfer500Mb(b *testing.B) {
156-
benchDataTransfer(setupEnv(b), 1024*1024*500)
227+
for n, bc := range bcs {
228+
b.Run(n, func(b *testing.B) {
229+
benchDataTransfer(setupEnv(b), 1024*1024*500, bc.m)
230+
})
231+
}
157232
}
158233

159234
func (b benchenv) benchHandshake() {

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ require (
99
github.com/libp2p/go-libp2p v0.8.1
1010
github.com/libp2p/go-libp2p-core v0.5.1
1111
github.com/multiformats/go-multiaddr v0.2.1
12+
github.com/stretchr/testify v1.5.1
1213
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5
13-
)
14+
)

handshake.go

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ package noise
33
import (
44
"context"
55
"crypto/rand"
6+
"encoding/binary"
67
"fmt"
8+
pool "github.com/libp2p/go-buffer-pool"
9+
"golang.org/x/crypto/poly1305"
710
"time"
811

912
"github.com/flynn/noise"
@@ -54,15 +57,21 @@ func (s *secureSession) runHandshake(ctx context.Context) error {
5457
// schedule the deadline removal once we're done handshaking.
5558
defer s.SetDeadline(time.Time{})
5659
}
57-
// TODO: else case (transport doesn't support native timeouts); spin off
58-
// a goroutine to monitor the context cancellation and pull the rug
59-
// from under by closing the connection altogether.
6060
}
6161

62+
// We can re-use this buffer for all handshake messages as it's size
63+
// will be the size of the maximum handshake message for the Noise XX pattern.
64+
// Also, since we prefix every noise handshake message with it's length, we need to account for
65+
// it when we fetch the buffer from the pool
66+
maxMsgSize := 2*noise.DH25519.DHLen() + len(payload) + 2*poly1305.TagSize
67+
hbuf := pool.Get(maxMsgSize + LengthPrefixLength)
68+
defer pool.Put(hbuf)
69+
6270
if s.initiator {
6371
// stage 0 //
6472
// do not send the payload just yet, as it would be plaintext; not secret.
65-
err = s.sendHandshakeMessage(hs, nil)
73+
// Handshake Msg Len = len(DH ephemeral key)
74+
err = s.sendHandshakeMessage(hs, nil, hbuf)
6675
if err != nil {
6776
return fmt.Errorf("error sending handshake message: %w", err)
6877
}
@@ -78,7 +87,8 @@ func (s *secureSession) runHandshake(ctx context.Context) error {
7887
}
7988

8089
// stage 2 //
81-
err = s.sendHandshakeMessage(hs, payload)
90+
// Handshake Msg Len = len(DHT static key) + MAC(static key is encrypted) + len(Payload) + MAC(payload is encrypted)
91+
err = s.sendHandshakeMessage(hs, payload, hbuf)
8292
if err != nil {
8393
return fmt.Errorf("error sending handshake message: %w", err)
8494
}
@@ -90,7 +100,9 @@ func (s *secureSession) runHandshake(ctx context.Context) error {
90100
}
91101

92102
// stage 1 //
93-
err = s.sendHandshakeMessage(hs, payload)
103+
// Handshake Msg Len = len(DH ephemeral key) + len(DHT static key) + MAC(static key is encrypted) + len(Payload) +
104+
//MAC(payload is encrypted)
105+
err = s.sendHandshakeMessage(hs, payload, hbuf)
94106
if err != nil {
95107
return fmt.Errorf("error sending handshake message: %w", err)
96108
}
@@ -129,13 +141,18 @@ func (s *secureSession) setCipherStates(cs1, cs2 *noise.CipherState) {
129141
// If payload is non-empty, it will be included in the handshake message.
130142
// If this is the final message in the sequence, calls setCipherStates
131143
// to initialize cipher states.
132-
func (s *secureSession) sendHandshakeMessage(hs *noise.HandshakeState, payload []byte) error {
133-
buf, cs1, cs2, err := hs.WriteMessage(nil, payload)
144+
func (s *secureSession) sendHandshakeMessage(hs *noise.HandshakeState, payload []byte, hbuf []byte) error {
145+
// the first two bytes will be the length of the noise handshake message.
146+
bz, cs1, cs2, err := hs.WriteMessage(hbuf[:LengthPrefixLength], payload)
134147
if err != nil {
135148
return err
136149
}
137150

138-
_, err = s.writeMsgInsecure(buf)
151+
// bz will also include the length prefix as we passed a slice of LengthPrefixLength length
152+
// to hs.Write().
153+
binary.BigEndian.PutUint16(bz, uint16(len(bz)-LengthPrefixLength))
154+
155+
_, err = s.writeMsgInsecure(bz)
139156
if err != nil {
140157
return err
141158
}
@@ -154,11 +171,19 @@ func (s *secureSession) sendHandshakeMessage(hs *noise.HandshakeState, payload [
154171
// If this is the final message in the sequence, it calls setCipherStates
155172
// to initialize cipher states.
156173
func (s *secureSession) readHandshakeMessage(hs *noise.HandshakeState) ([]byte, error) {
157-
raw, err := s.readMsgInsecure()
174+
l, err := s.readNextInsecureMsgLen()
158175
if err != nil {
159176
return nil, err
160177
}
161-
msg, cs1, cs2, err := hs.ReadMessage(nil, raw)
178+
179+
buf := pool.Get(l)
180+
defer pool.Put(buf)
181+
182+
if err := s.readNextMsgInsecure(buf); err != nil {
183+
return nil, err
184+
}
185+
186+
msg, cs1, cs2, err := hs.ReadMessage(nil, buf)
162187
if err != nil {
163188
return nil, err
164189
}

integration_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ func TestLibp2pIntegration(t *testing.T) {
128128
}
129129

130130
<-doneCh
131-
fmt.Println("fin")
132131
}
133132

134133
func writeRandomPayloadAndClose(t *testing.T, stream net.Stream) error {

0 commit comments

Comments
 (0)