From f73d400eb2486bbe69efb4e4864d705d0ecdcfe5 Mon Sep 17 00:00:00 2001 From: Alex Ciuba Date: Sun, 26 Jan 2014 15:08:05 -0500 Subject: [PATCH 1/6] Reduce string allocations --- msg.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/msg.go b/msg.go index 6d498ae2..ea8317e9 100644 --- a/msg.go +++ b/msg.go @@ -255,6 +255,7 @@ func packDomainName(s string, msg []byte, off int, compression map[string]int, c // Emit sequence of counted strings, chopping at dots. begin := 0 bs := []byte(s) + ro_bs, bs_fresh := s, true for i := 0; i < ls; i++ { if bs[i] == '\\' { for j := i; j < ls-1; j++ { @@ -274,6 +275,7 @@ func packDomainName(s string, msg []byte, off int, compression map[string]int, c } ls -= 2 } + bs_fresh = false continue } @@ -304,12 +306,16 @@ func packDomainName(s string, msg []byte, off int, compression map[string]int, c } off++ } + if compress && !bs_fresh { + ro_bs = string(bs) + bs_fresh = true + } // Dont try to compress '.' - if compression != nil && string(bs[begin:]) != "." { - if p, ok := compression[string(bs[begin:])]; !ok { + if compress && ro_bs[begin:] != "." { + if p, ok := compression[ro_bs[begin:]]; !ok { // Only offsets smaller than this can be used. if offset < maxCompressionOffset { - compression[string(bs[begin:])] = offset + compression[ro_bs[begin:]] = offset } } else { // The first hit is the longest matching dname From b8262501a858aa0e9823973b3484089cc433c1c4 Mon Sep 17 00:00:00 2001 From: Alex Ciuba Date: Sun, 26 Jan 2014 16:13:24 -0500 Subject: [PATCH 2/6] Minimize reflection calls --- msg.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/msg.go b/msg.go index ea8317e9..a90c538d 100644 --- a/msg.go +++ b/msg.go @@ -441,17 +441,19 @@ Loop: // slices and other (often anonymous) structs. func packStructValue(val reflect.Value, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { lenmsg := len(msg) - for i := 0; i < val.NumField(); i++ { - if val.Type().Field(i).Tag == `dns:"-"` { + numfield := val.NumField() + for i := 0; i < numfield; i++ { + typefield := val.Type().Field(i) + if typefield.Tag == `dns:"-"` { continue } switch fv := val.Field(i); fv.Kind() { default: return lenmsg, &Error{err: "bad kind packing"} case reflect.Slice: - switch val.Type().Field(i).Tag { + switch typefield.Tag { default: - return lenmsg, &Error{"bad tag packing slice: " + val.Type().Field(i).Tag.Get("dns")} + return lenmsg, &Error{"bad tag packing slice: " + typefield.Tag.Get("dns")} case `dns:"domain-name"`: for j := 0; j < val.Field(i).Len(); j++ { element := val.Field(i).Index(j).String() @@ -620,7 +622,7 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str msg[off+3] = byte(i) off += 4 case reflect.Uint64: - switch val.Type().Field(i).Tag { + switch typefield.Tag { default: if off+8 > lenmsg { return lenmsg, &Error{err: "overflow packing uint64"} @@ -653,9 +655,9 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str // There are multiple string encodings. // The tag distinguishes ordinary strings from domain names. s := fv.String() - switch val.Type().Field(i).Tag { + switch typefield.Tag { default: - return lenmsg, &Error{"bad tag packing string: " + val.Type().Field(i).Tag.Get("dns")} + return lenmsg, &Error{"bad tag packing string: " + typefield.Tag.Get("dns")} case `dns:"base64"`: b64, e := packBase64([]byte(s)) if e != nil { From 5a3bf3d74996d4be97ff6ab5e27699f90ef42d39 Mon Sep 17 00:00:00 2001 From: Alex Ciuba Date: Mon, 27 Jan 2014 17:33:14 -0500 Subject: [PATCH 3/6] Add more benchmarks --- dns_test.go | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/dns_test.go b/dns_test.go index 513b0ddc..e266c7eb 100644 --- a/dns_test.go +++ b/dns_test.go @@ -263,7 +263,6 @@ func BenchmarkMsgLen(b *testing.B) { } func BenchmarkMsgLenPack(b *testing.B) { - b.StopTimer() makeMsg := func(question string, ans, ns, e []RR) *Msg { msg := new(Msg) msg.SetQuestion(Fqdn(question), TypeANY) @@ -276,10 +275,38 @@ func BenchmarkMsgLenPack(b *testing.B) { name1 := "12345678901234567890123456789012345.12345678.123." rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1) msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil) - b.StartTimer() + b.ResetTimer() for i := 0; i < b.N; i++ { - b, _ := msg.Pack() - _ = len(b) + _, _ = msg.Pack() + } +} + +func BenchmarkMsgPackBuffer(b *testing.B) { + makeMsg := func(question string, ans, ns, e []RR) *Msg { + msg := new(Msg) + msg.SetQuestion(Fqdn(question), TypeANY) + msg.Answer = append(msg.Answer, ans...) + msg.Ns = append(msg.Ns, ns...) + msg.Extra = append(msg.Extra, e...) + msg.Compress = true + return msg + } + name1 := "12345678901234567890123456789012345.12345678.123." + rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1) + msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil) + buf := make([]byte, 512) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = msg.PackBuffer(buf) + } +} + +func BenchmarkPackDomainName(b *testing.B) { + name1 := "12345678901234567890123456789012345.12345678.123." + buf := make([]byte, len(name1)+1) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = PackDomainName(name1, buf, 0, nil, false) } } From 008dfb4ee81e8a0bf7cee3ee59dc87d5bc02aac7 Mon Sep 17 00:00:00 2001 From: Alex Ciuba Date: Mon, 27 Jan 2014 17:40:57 -0500 Subject: [PATCH 4/6] Fix benchmark alignment --- dns_test.go | 3 ++- server_test.go | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dns_test.go b/dns_test.go index e266c7eb..245e307c 100644 --- a/dns_test.go +++ b/dns_test.go @@ -242,7 +242,8 @@ func TestMsgLenTest(t *testing.T) { } } -func BenchmarkMsgLen(b *testing.B) { +// Padded to fix alignment +func BenchmarkMsgLen____(b *testing.B) { b.StopTimer() makeMsg := func(question string, ans, ns, e []RR) *Msg { msg := new(Msg) diff --git a/server_test.go b/server_test.go index a0f39d86..4aaf7b2b 100644 --- a/server_test.go +++ b/server_test.go @@ -65,7 +65,8 @@ func TestServing(t *testing.T) { } } -func BenchmarkServing(b *testing.B) { +// Padded to fix alignment +func BenchmarkServe____(b *testing.B) { b.StopTimer() HandleFunc("miek.nl.", HelloServer) a := runtime.GOMAXPROCS(4) @@ -92,7 +93,7 @@ func HelloServerCompress(w ResponseWriter, req *Msg) { w.WriteMsg(m) } -func BenchmarkServingCompress(b *testing.B) { +func BenchmarkServeCompress(b *testing.B) { b.StopTimer() HandleFunc("miek.nl.", HelloServerCompress) a := runtime.GOMAXPROCS(4) From 17ce0cd52fbd64f62638ae0bdb7c3a6a07d4d0be Mon Sep 17 00:00:00 2001 From: Alex Ciuba Date: Mon, 27 Jan 2014 19:45:45 -0500 Subject: [PATCH 5/6] Share the message buffer if we have room --- msg.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/msg.go b/msg.go index a90c538d..1c7a84a2 100644 --- a/msg.go +++ b/msg.go @@ -254,7 +254,15 @@ func packDomainName(s string, msg []byte, off int, compression map[string]int, c pointer := -1 // Emit sequence of counted strings, chopping at dots. begin := 0 - bs := []byte(s) + // We can share the message buffer because we don't need to look back. + // Keeping close makes tests more sensitive to failure (only 1 byte of room). + var bs []byte + if len(s) < len(msg) { + bs = msg[off+1:] + copy(bs, s) + } else { + bs = []byte(s) + } ro_bs, bs_fresh := s, true for i := 0; i < ls; i++ { if bs[i] == '\\' { From dc4eddaf9acd873ba8efd68e467cc9286f4591af Mon Sep 17 00:00:00 2001 From: Alex Ciuba Date: Mon, 27 Jan 2014 20:55:15 -0500 Subject: [PATCH 6/6] Add benchmark for message unpacking --- dns_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/dns_test.go b/dns_test.go index 245e307c..102ac138 100644 --- a/dns_test.go +++ b/dns_test.go @@ -302,6 +302,26 @@ func BenchmarkMsgPackBuffer(b *testing.B) { } } +func BenchmarkMsgUnpack(b *testing.B) { + makeMsg := func(question string, ans, ns, e []RR) *Msg { + msg := new(Msg) + msg.SetQuestion(Fqdn(question), TypeANY) + msg.Answer = append(msg.Answer, ans...) + msg.Ns = append(msg.Ns, ns...) + msg.Extra = append(msg.Extra, e...) + msg.Compress = true + return msg + } + name1 := "12345678901234567890123456789012345.12345678.123." + rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1) + msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil) + msg_buf, _ := msg.Pack() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = msg.Unpack(msg_buf) + } +} + func BenchmarkPackDomainName(b *testing.B) { name1 := "12345678901234567890123456789012345.12345678.123." buf := make([]byte, len(name1)+1)