@marcel91200 / caddy Mirror · read-only / commits / 4950ce48
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

Part 1: Optimize using compiler's inliner (#2687)

* optimized functions for inlining * added note regarding ResponseWriterWrapper * optimzed browseWrite* methods for FileServer * created benchmarks for comparison * creating browseListing instance in each function * created benchmarks for openResponseWriter * removed benchmarks of old implementations * implemented sync.Pool for byte buffers * using global sync.Pool for writing JSON/HTML

Dominik Braun committed Aug 8, 2019 at 07:59 UTC 4950ce485f7d931890fcfd2ee287b6df1b5db435
6 files changed +104 -10
caddy.go
+10 -2
@@ -208,8 +208,16 @@ func (d *Duration) UnmarshalJSON(b []byte) error {
208 // If no version information is available, a non-nil
209 // value will still be returned, but with an
210 // unknown version.
211 -func GoModule() debug.Module {
212 - mod := debug.Module{Version: "unknown"}
211 +func GoModule() *debug.Module {
212 + var mod debug.Module
213 + return goModule(&mod)
214 +}
215 +
216 +// goModule holds the actual implementation of GoModule.
217 +// Allocating debug.Module in GoModule() and passing a
218 +// reference to goModule enables mid-stack inlining.
219 +func goModule(mod *debug.Module) *debug.Module {
220 + mod.Version = "unknown"
221 bi, ok := debug.ReadBuildInfo()
222 if ok {
223 mod.Path = bi.Main.Path
modules/caddyhttp/encode/encode.go
+15 -6
@@ -93,14 +93,23 @@ func (enc *Encode) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyh
93 // encode the response with encodingName. The returned response writer MUST
94 // be closed after the handler completes.
95 func (enc *Encode) openResponseWriter(encodingName string, w http.ResponseWriter) *responseWriter {
96 + var rw responseWriter
97 + return enc.initResponseWriter(&rw, encodingName, w)
98 +}
99 +
100 +// initResponseWriter initializes the responseWriter instance
101 +// allocated in openResponseWriter, enabling mid-stack inlining.
102 +func (enc *Encode) initResponseWriter(rw *responseWriter, encodingName string, wrappedRW http.ResponseWriter) *responseWriter {
103 buf := bufPool.Get().(*bytes.Buffer)
104 buf.Reset()
98 - return &responseWriter{
99 - ResponseWriterWrapper: &caddyhttp.ResponseWriterWrapper{ResponseWriter: w},
100 - encodingName: encodingName,
101 - buf: buf,
102 - config: enc,
103 - }
105 +
106 + // The allocation of ResponseWriterWrapper might be optimized as well.
107 + rw.ResponseWriterWrapper = &caddyhttp.ResponseWriterWrapper{ResponseWriter: wrappedRW}
108 + rw.encodingName = encodingName
109 + rw.buf = buf
110 + rw.config = enc
111 +
112 + return rw
113 }
114
115 // responseWriter writes to an underlying response writer
modules/caddyhttp/encode/encode_test.go new
+12
@@ -0,0 +1,12 @@
1 +package encode
2 +
3 +import (
4 + "testing"
5 +)
6 +
7 +func BenchmarkOpenResponseWriter(b *testing.B) {
8 + enc := new(Encode)
9 + for n := 0; n < b.N; n++ {
10 + enc.openResponseWriter("test", nil)
11 + }
12 +}
modules/caddyhttp/fileserver/browse.go
+4 -2
@@ -133,14 +133,16 @@ func (fsrv *FileServer) browseApplyQueryParams(w http.ResponseWriter, r *http.Re
133 }
134
135 func (fsrv *FileServer) browseWriteJSON(listing browseListing) (*bytes.Buffer, error) {
136 - buf := new(bytes.Buffer)
136 + buf := bufPool.Get().(*bytes.Buffer)
137 err := json.NewEncoder(buf).Encode(listing.Items)
138 + bufPool.Put(buf)
139 return buf, err
140 }
141
142 func (fsrv *FileServer) browseWriteHTML(listing browseListing) (*bytes.Buffer, error) {
142 - buf := new(bytes.Buffer)
143 + buf := bufPool.Get().(*bytes.Buffer)
144 err := fsrv.Browse.template.Execute(buf, listing)
145 + bufPool.Put(buf)
146 return buf, err
147 }
148
modules/caddyhttp/fileserver/browse_test.go new
+54
@@ -0,0 +1,54 @@
1 +package fileserver
2 +
3 +import (
4 + "html/template"
5 + "testing"
6 +
7 + "github.com/caddyserver/caddy/v2"
8 +)
9 +
10 +func BenchmarkBrowseWriteJSON(b *testing.B) {
11 + fsrv := new(FileServer)
12 + fsrv.Provision(caddy.Context{})
13 + listing := browseListing{
14 + Name: "test",
15 + Path: "test",
16 + CanGoUp: false,
17 + Items: make([]fileInfo, 100),
18 + NumDirs: 42,
19 + NumFiles: 420,
20 + Sort: "",
21 + Order: "",
22 + ItemsLimitedTo: 42,
23 + }
24 + b.ResetTimer()
25 +
26 + for n := 0; n < b.N; n++ {
27 + fsrv.browseWriteJSON(listing)
28 + }
29 +}
30 +
31 +func BenchmarkBrowseWriteHTML(b *testing.B) {
32 + fsrv := new(FileServer)
33 + fsrv.Provision(caddy.Context{})
34 + fsrv.Browse = &Browse{
35 + TemplateFile: "",
36 + template: template.New("test"),
37 + }
38 + listing := browseListing{
39 + Name: "test",
40 + Path: "test",
41 + CanGoUp: false,
42 + Items: make([]fileInfo, 100),
43 + NumDirs: 42,
44 + NumFiles: 420,
45 + Sort: "",
46 + Order: "",
47 + ItemsLimitedTo: 42,
48 + }
49 + b.ResetTimer()
50 +
51 + for n := 0; n < b.N; n++ {
52 + fsrv.browseWriteHTML(listing)
53 + }
54 +}
modules/caddyhttp/fileserver/staticfiles.go
+9
@@ -15,6 +15,7 @@
15 package fileserver
16
17 import (
18 + "bytes"
19 "fmt"
20 "html/template"
21 weakrand "math/rand"
@@ -25,6 +26,7 @@ import (
26 "path/filepath"
27 "strconv"
28 "strings"
29 + "sync"
30 "time"
31
32 "github.com/caddyserver/caddy/v2"
@@ -46,6 +48,7 @@ type FileServer struct {
48 Hide []string `json:"hide,omitempty"`
49 IndexNames []string `json:"index_names,omitempty"`
50 Browse *Browse `json:"browse,omitempty"`
51 +
52 // TODO: Content negotiation
53 }
54
@@ -301,6 +304,12 @@ func calculateEtag(d os.FileInfo) string {
304
305 var defaultIndexNames = []string{"index.html"}
306
307 +var bufPool = sync.Pool{
308 + New: func() interface{} {
309 + return new(bytes.Buffer)
310 + },
311 +}
312 +
313 const minBackoff, maxBackoff = 2, 5
314
315 // Interface guards