mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-27 06:03:48 +03:00
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
This commit is contained in:
parent
c8b0a97b1c
commit
4950ce485f
6 changed files with 104 additions and 10 deletions
12
caddy.go
12
caddy.go
|
@ -208,8 +208,16 @@ func (d *Duration) UnmarshalJSON(b []byte) error {
|
||||||
// If no version information is available, a non-nil
|
// If no version information is available, a non-nil
|
||||||
// value will still be returned, but with an
|
// value will still be returned, but with an
|
||||||
// unknown version.
|
// unknown version.
|
||||||
func GoModule() debug.Module {
|
func GoModule() *debug.Module {
|
||||||
mod := debug.Module{Version: "unknown"}
|
var mod debug.Module
|
||||||
|
return goModule(&mod)
|
||||||
|
}
|
||||||
|
|
||||||
|
// goModule holds the actual implementation of GoModule.
|
||||||
|
// Allocating debug.Module in GoModule() and passing a
|
||||||
|
// reference to goModule enables mid-stack inlining.
|
||||||
|
func goModule(mod *debug.Module) *debug.Module {
|
||||||
|
mod.Version = "unknown"
|
||||||
bi, ok := debug.ReadBuildInfo()
|
bi, ok := debug.ReadBuildInfo()
|
||||||
if ok {
|
if ok {
|
||||||
mod.Path = bi.Main.Path
|
mod.Path = bi.Main.Path
|
||||||
|
|
|
@ -93,14 +93,23 @@ func (enc *Encode) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyh
|
||||||
// encode the response with encodingName. The returned response writer MUST
|
// encode the response with encodingName. The returned response writer MUST
|
||||||
// be closed after the handler completes.
|
// be closed after the handler completes.
|
||||||
func (enc *Encode) openResponseWriter(encodingName string, w http.ResponseWriter) *responseWriter {
|
func (enc *Encode) openResponseWriter(encodingName string, w http.ResponseWriter) *responseWriter {
|
||||||
|
var rw responseWriter
|
||||||
|
return enc.initResponseWriter(&rw, encodingName, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initResponseWriter initializes the responseWriter instance
|
||||||
|
// allocated in openResponseWriter, enabling mid-stack inlining.
|
||||||
|
func (enc *Encode) initResponseWriter(rw *responseWriter, encodingName string, wrappedRW http.ResponseWriter) *responseWriter {
|
||||||
buf := bufPool.Get().(*bytes.Buffer)
|
buf := bufPool.Get().(*bytes.Buffer)
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
return &responseWriter{
|
|
||||||
ResponseWriterWrapper: &caddyhttp.ResponseWriterWrapper{ResponseWriter: w},
|
// The allocation of ResponseWriterWrapper might be optimized as well.
|
||||||
encodingName: encodingName,
|
rw.ResponseWriterWrapper = &caddyhttp.ResponseWriterWrapper{ResponseWriter: wrappedRW}
|
||||||
buf: buf,
|
rw.encodingName = encodingName
|
||||||
config: enc,
|
rw.buf = buf
|
||||||
}
|
rw.config = enc
|
||||||
|
|
||||||
|
return rw
|
||||||
}
|
}
|
||||||
|
|
||||||
// responseWriter writes to an underlying response writer
|
// responseWriter writes to an underlying response writer
|
||||||
|
|
12
modules/caddyhttp/encode/encode_test.go
Normal file
12
modules/caddyhttp/encode/encode_test.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package encode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkOpenResponseWriter(b *testing.B) {
|
||||||
|
enc := new(Encode)
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
enc.openResponseWriter("test", nil)
|
||||||
|
}
|
||||||
|
}
|
|
@ -133,14 +133,16 @@ func (fsrv *FileServer) browseApplyQueryParams(w http.ResponseWriter, r *http.Re
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fsrv *FileServer) browseWriteJSON(listing browseListing) (*bytes.Buffer, error) {
|
func (fsrv *FileServer) browseWriteJSON(listing browseListing) (*bytes.Buffer, error) {
|
||||||
buf := new(bytes.Buffer)
|
buf := bufPool.Get().(*bytes.Buffer)
|
||||||
err := json.NewEncoder(buf).Encode(listing.Items)
|
err := json.NewEncoder(buf).Encode(listing.Items)
|
||||||
|
bufPool.Put(buf)
|
||||||
return buf, err
|
return buf, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fsrv *FileServer) browseWriteHTML(listing browseListing) (*bytes.Buffer, error) {
|
func (fsrv *FileServer) browseWriteHTML(listing browseListing) (*bytes.Buffer, error) {
|
||||||
buf := new(bytes.Buffer)
|
buf := bufPool.Get().(*bytes.Buffer)
|
||||||
err := fsrv.Browse.template.Execute(buf, listing)
|
err := fsrv.Browse.template.Execute(buf, listing)
|
||||||
|
bufPool.Put(buf)
|
||||||
return buf, err
|
return buf, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
54
modules/caddyhttp/fileserver/browse_test.go
Normal file
54
modules/caddyhttp/fileserver/browse_test.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package fileserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/caddyserver/caddy/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkBrowseWriteJSON(b *testing.B) {
|
||||||
|
fsrv := new(FileServer)
|
||||||
|
fsrv.Provision(caddy.Context{})
|
||||||
|
listing := browseListing{
|
||||||
|
Name: "test",
|
||||||
|
Path: "test",
|
||||||
|
CanGoUp: false,
|
||||||
|
Items: make([]fileInfo, 100),
|
||||||
|
NumDirs: 42,
|
||||||
|
NumFiles: 420,
|
||||||
|
Sort: "",
|
||||||
|
Order: "",
|
||||||
|
ItemsLimitedTo: 42,
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
fsrv.browseWriteJSON(listing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkBrowseWriteHTML(b *testing.B) {
|
||||||
|
fsrv := new(FileServer)
|
||||||
|
fsrv.Provision(caddy.Context{})
|
||||||
|
fsrv.Browse = &Browse{
|
||||||
|
TemplateFile: "",
|
||||||
|
template: template.New("test"),
|
||||||
|
}
|
||||||
|
listing := browseListing{
|
||||||
|
Name: "test",
|
||||||
|
Path: "test",
|
||||||
|
CanGoUp: false,
|
||||||
|
Items: make([]fileInfo, 100),
|
||||||
|
NumDirs: 42,
|
||||||
|
NumFiles: 420,
|
||||||
|
Sort: "",
|
||||||
|
Order: "",
|
||||||
|
ItemsLimitedTo: 42,
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
fsrv.browseWriteHTML(listing)
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
package fileserver
|
package fileserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
weakrand "math/rand"
|
weakrand "math/rand"
|
||||||
|
@ -25,6 +26,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
"github.com/caddyserver/caddy/v2"
|
||||||
|
@ -46,6 +48,7 @@ type FileServer struct {
|
||||||
Hide []string `json:"hide,omitempty"`
|
Hide []string `json:"hide,omitempty"`
|
||||||
IndexNames []string `json:"index_names,omitempty"`
|
IndexNames []string `json:"index_names,omitempty"`
|
||||||
Browse *Browse `json:"browse,omitempty"`
|
Browse *Browse `json:"browse,omitempty"`
|
||||||
|
|
||||||
// TODO: Content negotiation
|
// TODO: Content negotiation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,6 +304,12 @@ func calculateEtag(d os.FileInfo) string {
|
||||||
|
|
||||||
var defaultIndexNames = []string{"index.html"}
|
var defaultIndexNames = []string{"index.html"}
|
||||||
|
|
||||||
|
var bufPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return new(bytes.Buffer)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
const minBackoff, maxBackoff = 2, 5
|
const minBackoff, maxBackoff = 2, 5
|
||||||
|
|
||||||
// Interface guards
|
// Interface guards
|
||||||
|
|
Loading…
Reference in a new issue