Skip to content

Commit 8487663

Browse files
cezarsamcuadros
authored andcommitted
Major performance improvement in UDP processing by reusing buffer (#33)
* Connections as PacketConn slice, avoid runtime type assertions * Add benchmarks for message processing for TCP and Datagram connections The reason we can't simply use ListenUDP() and Dial() in the datagram benchmark is that we wouldn't be able to ensure that all messages would arrive on the server, even for a udp connection to localhost. By using the fakePacketConn we ensure that all messages are delivered making the datagram benchmark reliable. * Major performance improvement in UDP processing by reusing buffers. As we can see in the added benchmarks receiving messages from a UDP connection used to be very slow, slower than using TCP which was a little strange. By simply reusing the buffer used to receive datagrams we can see a major improvement in processing time and memory allocations. benchmark old ns/op new ns/op delta BenchmarkDatagramNoFormatting-4 16172 2668 -83.50% BenchmarkTCPNoFormatting-4 7682 7644 -0.49% benchmark old MB/s new MB/s speedup BenchmarkDatagramNoFormatting-4 2.91 17.62 6.05x BenchmarkTCPNoFormatting-4 6.12 6.15 1.00x benchmark old allocs new allocs delta BenchmarkDatagramNoFormatting-4 5 4 -20.00% BenchmarkTCPNoFormatting-4 6 6 +0.00% benchmark old bytes new bytes delta BenchmarkDatagramNoFormatting-4 65905 368 -99.44% BenchmarkTCPNoFormatting-4 464 464 +0.00%
1 parent b579cfc commit 8487663

File tree

2 files changed

+108
-7
lines changed

2 files changed

+108
-7
lines changed

server.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type TlsPeerNameFunc func(tlsConn *tls.Conn) (tlsPeer string, ok bool)
3030

3131
type Server struct {
3232
listeners []net.Listener
33-
connections []net.Conn
33+
connections []net.PacketConn
3434
wait sync.WaitGroup
3535
doneTcp chan bool
3636
datagramChannel chan DatagramMessage
@@ -315,16 +315,12 @@ type DatagramMessage struct {
315315
client string
316316
}
317317

318-
func (s *Server) goReceiveDatagrams(connection net.Conn) {
319-
packetconn, ok := connection.(net.PacketConn)
320-
if !ok {
321-
panic("Connection is not a packet connection")
322-
}
318+
func (s *Server) goReceiveDatagrams(packetconn net.PacketConn) {
323319
s.wait.Add(1)
324320
go func() {
325321
defer s.wait.Done()
322+
buf := make([]byte, 65536)
326323
for {
327-
buf := make([]byte, 65536)
328324
n, addr, err := packetconn.ReadFrom(buf)
329325
if err == nil {
330326
// Ignore trailing control characters and NULs

server_bench_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package syslog
2+
3+
import (
4+
"bufio"
5+
"io"
6+
"net"
7+
"testing"
8+
"time"
9+
10+
"gopkg.in/mcuadros/go-syslog.v2/format"
11+
)
12+
13+
type noopFormatter struct{}
14+
15+
func (noopFormatter) Parse() error {
16+
return nil
17+
}
18+
19+
func (noopFormatter) Dump() format.LogParts {
20+
return format.LogParts{}
21+
}
22+
23+
func (noopFormatter) Location(*time.Location) {}
24+
25+
func (n noopFormatter) GetParser(l []byte) format.LogParser {
26+
return n
27+
}
28+
29+
func (n noopFormatter) GetSplitFunc() bufio.SplitFunc {
30+
return nil
31+
}
32+
33+
type handlerCounter struct {
34+
expected int
35+
current int
36+
done chan struct{}
37+
}
38+
39+
func (s *handlerCounter) Handle(logParts format.LogParts, msgLen int64, err error) {
40+
s.current++
41+
if s.current == s.expected {
42+
close(s.done)
43+
}
44+
}
45+
46+
type fakePacketConn struct {
47+
*io.PipeReader
48+
}
49+
50+
func (c *fakePacketConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
51+
n, err = c.PipeReader.Read(b)
52+
return
53+
}
54+
func (c *fakePacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
55+
return 0, nil
56+
}
57+
func (c *fakePacketConn) Close() error {
58+
return nil
59+
}
60+
func (c *fakePacketConn) LocalAddr() net.Addr {
61+
return nil
62+
}
63+
func (c *fakePacketConn) SetDeadline(t time.Time) error {
64+
return nil
65+
}
66+
func (c *fakePacketConn) SetReadDeadline(t time.Time) error {
67+
return nil
68+
}
69+
func (c *fakePacketConn) SetWriteDeadline(t time.Time) error {
70+
return nil
71+
}
72+
73+
func BenchmarkDatagramNoFormatting(b *testing.B) {
74+
handler := &handlerCounter{expected: b.N, done: make(chan struct{})}
75+
server := NewServer()
76+
defer server.Kill()
77+
server.SetFormat(noopFormatter{})
78+
server.SetHandler(handler)
79+
reader, writer := io.Pipe()
80+
server.goReceiveDatagrams(&fakePacketConn{PipeReader: reader})
81+
server.goParseDatagrams()
82+
msg := []byte(exampleSyslog + "\n")
83+
b.SetBytes(int64(len(msg)))
84+
for i := 0; i < b.N; i++ {
85+
writer.Write(msg)
86+
}
87+
<-handler.done
88+
}
89+
90+
func BenchmarkTCPNoFormatting(b *testing.B) {
91+
handler := &handlerCounter{expected: b.N, done: make(chan struct{})}
92+
server := NewServer()
93+
defer server.Kill()
94+
server.SetFormat(noopFormatter{})
95+
server.SetHandler(handler)
96+
server.ListenTCP("127.0.0.1:0")
97+
server.Boot()
98+
conn, _ := net.DialTimeout("tcp", server.listeners[0].Addr().String(), time.Second)
99+
msg := []byte(exampleSyslog + "\n")
100+
b.SetBytes(int64(len(msg)))
101+
for i := 0; i < b.N; i++ {
102+
conn.Write(msg)
103+
}
104+
<-handler.done
105+
}

0 commit comments

Comments
 (0)