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