Skip to content

Commit 90e68ae

Browse files
committed
app: refactor e2e TestTUN to reduce scheduler overhead on channels
1 parent bad2e1d commit 90e68ae

3 files changed

Lines changed: 91 additions & 60 deletions

File tree

application_simnet_test.go

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package awl
33
import (
44
"fmt"
55
"os"
6-
"sync/atomic"
76
"testing"
87
"time"
98

@@ -40,15 +39,26 @@ func TestSimulatedTunnelPerformance(t *testing.T) {
4039
bandwidthMbps int
4140
}{
4241
{
43-
name: "Fiber_300Mbps_1ms",
42+
name: "Fiber_200Mbps_1ms",
4443
latency: 1 * time.Millisecond,
45-
bandwidthMbps: 300 * Mbps,
44+
bandwidthMbps: 200 * Mbps,
4645
},
4746
{
48-
name: "LongDistFiber_300Mbps_200ms",
47+
name: "LongDistFiber_200Mbps_200ms",
4948
latency: 200 * time.Millisecond,
50-
bandwidthMbps: 300 * Mbps,
49+
bandwidthMbps: 200 * Mbps,
5150
},
51+
{
52+
name: "Fiber_100Mbps_1ms",
53+
latency: 1 * time.Millisecond,
54+
bandwidthMbps: 100 * Mbps,
55+
},
56+
{
57+
name: "LongDistFiber_100Mbps_200ms",
58+
latency: 200 * time.Millisecond,
59+
bandwidthMbps: 100 * Mbps,
60+
},
61+
5262
{
5363
name: "Cable_10Mbps_1ms",
5464
latency: 1 * time.Millisecond,
@@ -74,6 +84,7 @@ func TestSimulatedTunnelPerformance(t *testing.T) {
7484
latency: 300 * time.Millisecond,
7585
bandwidthMbps: 10 * Mbps,
7686
},
87+
7788
{
7889
name: "Cable_50Mbps_1ms",
7990
latency: 1 * time.Millisecond,
@@ -99,6 +110,7 @@ func TestSimulatedTunnelPerformance(t *testing.T) {
99110
latency: 300 * time.Millisecond,
100111
bandwidthMbps: 50 * Mbps,
101112
},
113+
102114
{
103115
name: "DSL_20Mbps_25ms",
104116
latency: 25 * time.Millisecond,
@@ -126,7 +138,7 @@ func TestSimulatedTunnelPerformance(t *testing.T) {
126138
// TODO: try with different packet sizes
127139
// we probably should aim for one packet per UDP datagram
128140
const packetSize = 3500 // Typical VPN packet size
129-
const testDuration = 10 * time.Second
141+
const testDuration = 30 * time.Second
130142

131143
// Setup link properties for the simulation
132144
// We simulate a symmetric link between the two peers
@@ -136,6 +148,8 @@ func TestSimulatedTunnelPerformance(t *testing.T) {
136148
}
137149

138150
// Create two peers
151+
// uncomment to debug QUIC events
152+
// t.Setenv("QLOGDIR", "./test-simnet")
139153
ctx := t.Context()
140154
extraLibp2pOpts := []libp2p.Option{
141155
simlibp2p.QUICSimnet(net, linkSettings),
@@ -156,10 +170,14 @@ func TestSimulatedTunnelPerformance(t *testing.T) {
156170
peer2.tun.ClearInboundCount()
157171

158172
// Send packets
159-
var packetsSent int64
160173
done := make(chan struct{})
161174
startTime := time.Now()
162175

176+
packetsBatch := make([][]byte, TestTUNBatchSize*10)
177+
for i := range packetsBatch {
178+
packetsBatch[i] = packet
179+
}
180+
163181
go func() {
164182
defer close(done)
165183
timer := time.NewTimer(testDuration)
@@ -171,22 +189,11 @@ func TestSimulatedTunnelPerformance(t *testing.T) {
171189
return
172190
case <-ctx.Done():
173191
return
174-
default:
192+
case peer1.tun.Outbound <- packetsBatch:
175193
// ok
176194
}
177195

178-
// Send with "batches" to minimize runtime overhead for select
179-
for range 10 {
180-
peer1.tun.Outbound <- packet
181-
atomic.AddInt64(&packetsSent, 1)
182-
}
183-
184-
// TODO:
185-
// Slight throttle to keep drops lower
186-
// const sleepEvery = 200
187-
// if newCount != 0 && newCount%sleepEvery == 0 {
188-
// time.Sleep(1 * time.Millisecond)
189-
// }
196+
// TODO: add ratelimit for TestTUN to remove busyloop
190197
}
191198
}()
192199

@@ -199,7 +206,7 @@ func TestSimulatedTunnelPerformance(t *testing.T) {
199206

200207
// Collect metrics
201208
received := peer2.tun.InboundCount()
202-
sent := atomic.LoadInt64(&packetsSent)
209+
sent := peer1.tun.OutboundCount()
203210

204211
packetLoss := (float64(1) - float64(received)/float64(sent)) * 100
205212

@@ -219,6 +226,9 @@ func TestSimulatedTunnelPerformance(t *testing.T) {
219226
// TODO: calculate p50/p95/p99 latency, jitter
220227
})
221228
})
229+
230+
// cool down a bit
231+
time.Sleep(time.Second)
222232
}
223233

224234
table.Render()

application_test.go

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"runtime"
1111
"strings"
1212
"sync"
13-
"sync/atomic"
1413
"testing"
1514
"time"
1615

@@ -583,7 +582,7 @@ func TestUpdatePeerSettingsIPAddr(t *testing.T) {
583582
// Send packets from peer1 to peer2
584583
packet := testPacketWithDest(packetSize, newIP)
585584
for i := 0; i < packetsCount; i++ {
586-
peer1.tun.Outbound <- packet
585+
peer1.tun.Outbound <- [][]byte{packet}
587586
}
588587

589588
// Wait for packet processing
@@ -681,14 +680,15 @@ func TestTunnelPackets(t *testing.T) {
681680
sendPackets := func(peer, peerWithInbound TestPeer) {
682681
defer wg.Done()
683682
packet := testPacket(packetSize)
683+
packetsBatch := make([][]byte, TestTUNBatchSize)
684+
for i := range packetsBatch {
685+
packetsBatch[i] = packet
686+
}
684687

685-
for i := 0; i < packetsCount; i++ {
686-
peer.tun.Outbound <- packet
687-
// to don't have packets loss
688-
inbound := peerWithInbound.tun.InboundCount()
689-
if (int64(i) - inbound) >= 50 {
690-
time.Sleep(50 * time.Millisecond)
691-
}
688+
for i := 0; i < packetsCount/TestTUNBatchSize; i++ {
689+
peer.tun.Outbound <- packetsBatch
690+
// to avoid packet loss
691+
time.Sleep(100 * time.Millisecond)
692692
}
693693
}
694694

@@ -697,7 +697,7 @@ func TestTunnelPackets(t *testing.T) {
697697
go sendPackets(peer2, peer1)
698698
wg.Wait()
699699

700-
time.Sleep(1 * time.Second)
700+
time.Sleep(2 * time.Second)
701701
received1 := peer1.tun.InboundCount()
702702
received2 := peer2.tun.InboundCount()
703703
ts.EqualValues(packetsCount, received1)
@@ -717,21 +717,24 @@ func BenchmarkTunnelPackets(b *testing.B) {
717717
b.ResetTimer()
718718

719719
b.SetBytes(int64(packetSize))
720-
var packetsSent int64
721720
packet := testPacket(packetSize)
722721
peer2.tun.ReferenceInboundPacketLen = len(packet)
723722
peer2.tun.ClearInboundCount()
723+
packetsBatch := make([][]byte, TestTUNBatchSize*10)
724+
for i := range packetsBatch {
725+
packetsBatch[i] = packet
726+
}
727+
724728
for i := 0; i < b.N; i++ {
725-
peer1.tun.Outbound <- packet
726-
atomic.AddInt64(&packetsSent, 1)
729+
peer1.tun.Outbound <- packetsBatch
727730
// to have packet_loss at reasonable level (but more than 0)
728731
const sleepEvery = 100
729732
if i != 0 && i%sleepEvery == 0 {
730733
time.Sleep(1 * time.Millisecond)
731734
}
732735
}
733736
received := peer2.tun.InboundCount()
734-
sent := atomic.LoadInt64(&packetsSent)
737+
sent := peer1.tun.OutboundCount()
735738
packetLoss := (float64(1) - float64(received)/float64(sent)) * 100
736739
bandwidth := float64(received) * float64(packetSize) / 1024 / 1024
737740
b.ReportMetric(bandwidth, "MB/s")

test_suite_test.go

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ import (
3535
"github.com/anywherelan/awl/vpn"
3636
)
3737

38+
const TestTUNBatchSize = 100
39+
3840
func init() {
3941
// TODO: move to config
4042
useAwldns = false
@@ -116,6 +118,8 @@ func (ts *TestSuite) newTestPeer(disableLogging bool, listenAddrs []multiaddr.Mu
116118
app.Conf.HttpListenOnAdminHost = false
117119
app.Conf.SetListenAddresses(listenAddrs)
118120
app.Conf.P2pNode.BootstrapPeers = ts.bootstrapAddrsStr
121+
app.Conf.P2pNode.ParallelSendingStreamsCount = 1
122+
app.Conf.P2pNode.UseDedicatedConnForEachStream = false
119123
if ts.isSimnet {
120124
app.Conf.SOCKS5 = config.SOCKS5Config{
121125
ListenerEnabled: false,
@@ -256,28 +260,36 @@ func (ts *TestSuite) sendAndAcceptFriendRequest(peer1, peer2 TestPeer) {
256260
}
257261

258262
type TestTUN struct {
259-
Outbound chan []byte
263+
Outbound chan [][]byte
264+
outboundBuf [][]byte
260265
ReferenceInboundPacketLen int
261266

262-
inboundCount int64
263-
closed chan struct{}
264-
events chan tun.Event
265-
tun testTun
267+
inboundCount int64
268+
outboundCount int64
269+
isClosed atomic.Bool
270+
events chan tun.Event
271+
tun *testTun
266272
}
267273

268274
func NewTestTUN() *TestTUN {
269275
c := &TestTUN{
270-
Outbound: make(chan []byte),
271-
closed: make(chan struct{}),
276+
Outbound: make(chan [][]byte),
272277
events: make(chan tun.Event, 1),
278+
tun: &testTun{},
273279
}
274-
c.tun.t = c
275280
c.events <- tun.EventUp
281+
282+
c.tun.t = c
283+
276284
return c
277285
}
278286

279287
func (c *TestTUN) TUN() tun.Device {
280-
return &c.tun
288+
return c.tun
289+
}
290+
291+
func (c *TestTUN) OutboundCount() int64 {
292+
return atomic.LoadInt64(&c.outboundCount)
281293
}
282294

283295
func (c *TestTUN) InboundCount() int64 {
@@ -295,31 +307,37 @@ type testTun struct {
295307
func (t *testTun) File() *os.File { return nil }
296308

297309
func (t *testTun) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) {
298-
for i, buf := range bufs {
299-
select {
300-
case <-t.t.closed:
301-
return n, os.ErrClosed
302-
case msg := <-t.t.Outbound:
303-
copyN := copy(buf[offset:], msg)
304-
sizes[i] = copyN
305-
n++
306-
}
310+
if t.t.isClosed.Load() {
311+
return 0, os.ErrClosed
312+
}
313+
if len(t.t.outboundBuf) == 0 {
314+
t.t.outboundBuf = <-t.t.Outbound
307315
}
308316

317+
for i := range min(len(bufs), len(t.t.outboundBuf)) {
318+
outboundMsg := t.t.outboundBuf[i]
319+
copyN := copy(bufs[i][offset:], outboundMsg)
320+
sizes[i] = copyN
321+
n++
322+
}
323+
324+
t.t.outboundBuf = t.t.outboundBuf[n:]
325+
atomic.AddInt64(&t.t.outboundCount, int64(n))
326+
309327
return n, nil
310328
}
311329

312330
func (t *testTun) Write(bufs [][]byte, offset int) (n int, err error) {
331+
if t.t.isClosed.Load() {
332+
return 0, os.ErrClosed
333+
}
334+
313335
for _, buf := range bufs {
314336
msg := buf[offset:]
315337
if len(msg) != t.t.ReferenceInboundPacketLen {
316338
return n, errors.New("packets length mismatch")
317339
}
318-
select {
319-
case <-t.t.closed:
320-
return n, os.ErrClosed
321-
default:
322-
}
340+
323341
atomic.AddInt64(&t.t.inboundCount, 1)
324342
n++
325343
}
@@ -328,15 +346,15 @@ func (t *testTun) Write(bufs [][]byte, offset int) (n int, err error) {
328346
}
329347

330348
func (t *testTun) BatchSize() int {
331-
return 1
349+
return TestTUNBatchSize
332350
}
333351

334352
func (t *testTun) Flush() error { return nil }
335353
func (t *testTun) MTU() (int, error) { return vpn.InterfaceMTU, nil }
336354
func (t *testTun) Name() (string, error) { return "testTun", nil }
337355
func (t *testTun) Events() <-chan tun.Event { return t.t.events }
338356
func (t *testTun) Close() error {
339-
close(t.t.closed)
357+
t.t.isClosed.Store(true)
340358
close(t.t.events)
341359
return nil
342360
}

0 commit comments

Comments
 (0)