Skip to content

Commit 9d4d68a

Browse files
author
Luca Buzzi
committed
Optimize view rendering with sync.Pool for buffer reuse and improved performance
- introduced a global sync.Pool for reusing *bytes.Buffer instances to reduce GC pressure and improve memory efficiency - updated Request.View and Request.RenderView to utilize the buffer pool - ensured proper buffer cleanup with defer and Reset before returning buffers to the pool - replaced per-view buffer instantiation with pooled buffers and added logic to handle intermediate view rendering - preserved previous functionality while enhancing performance and reducing memory allocations
1 parent 4b2f116 commit 9d4d68a

File tree

1 file changed

+42
-19
lines changed

1 file changed

+42
-19
lines changed

evo.context.go

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import (
44
"bytes"
55
"encoding/json"
66
"fmt"
7-
"github.com/iesreza/jet/v8"
87
"net/url"
98
"reflect"
109
"strings"
10+
"sync"
1111
"time"
1212

13+
"github.com/iesreza/jet/v8"
14+
1315
e "github.com/getevo/evo/errors"
1416
"github.com/getevo/evo/lib/jwt"
1517
"github.com/getevo/evo/lib/log"
@@ -49,6 +51,12 @@ type URL struct {
4951
Raw string
5052
}
5153

54+
var bufferPool = sync.Pool{
55+
New: func() interface{} {
56+
return new(bytes.Buffer)
57+
},
58+
}
59+
5260
func (response Response) HasError() bool {
5361
return response.Error.Exist()
5462
}
@@ -138,9 +146,11 @@ func (r *Request) Persist() {
138146

139147
func (r *Request) View(mixed ...interface{}) {
140148
buff := r.RenderView(mixed...)
141-
buff.Bytes()
149+
defer func() {
150+
buff.Reset()
151+
bufferPool.Put(buff)
152+
}()
142153
r.SendHTML(buff.Bytes())
143-
buff = nil
144154
}
145155

146156
type View func(*Request, jet.VarMap) []interface{}
@@ -203,7 +213,7 @@ func (r *Request) RenderView(mixed ...interface{}) *bytes.Buffer {
203213
input = ref.Interface()
204214
}
205215
}
206-
var buff bytes.Buffer
216+
207217
vars.Set("base", r.Context.Protocol()+"://"+r.Context.Hostname())
208218
vars.Set("proto", r.Context.Protocol())
209219
vars.Set("hostname", r.Context.Hostname())
@@ -234,27 +244,40 @@ func (r *Request) RenderView(mixed ...interface{}) *bytes.Buffer {
234244
vars.Set(key, val)
235245
}
236246

237-
for _, view := range views {
238-
buff = bytes.Buffer{}
247+
buff := bufferPool.Get().(*bytes.Buffer)
248+
buff.Reset()
249+
250+
tmpBuff := bufferPool.Get().(*bytes.Buffer)
251+
tmpBuff.Reset()
252+
defer bufferPool.Put(tmpBuff)
253+
for i, view := range views {
239254
parts := strings.Split(view, ".")
255+
if len(parts) <= 1 {
256+
continue
257+
}
240258

241-
if len(parts) > 1 {
242-
t, err := GetView(parts[0], strings.Join(parts[1:], "."))
243-
if err == nil {
244-
err = t.Execute(&buff, vars, map[string]interface{}{})
245-
if err != nil {
246-
log.Error(err)
247-
}
248-
} else {
259+
t, err := GetView(parts[0], strings.Join(parts[1:], "."))
260+
if err != nil {
261+
log.Error(err)
262+
continue
263+
}
264+
265+
// Use a temporary buffer for all views except the last one
266+
if i < len(views)-1 {
267+
tmpBuff.Reset()
268+
if err := t.Execute(tmpBuff, vars, nil); err != nil {
269+
log.Error(err)
270+
}
271+
vars.Set("body", tmpBuff.Bytes()) // intermediate result for use in next views
272+
} else {
273+
// Final view writes to main buffer
274+
if err := t.Execute(buff, vars, nil); err != nil {
249275
log.Error(err)
250-
log.Error(parts)
251276
}
252-
vars.Set("body", buff.Bytes())
253-
254277
}
255278
}
256-
vars = nil
257-
return &buff
279+
280+
return buff
258281
}
259282

260283
func (r *Request) Cached(duration time.Duration, key ...string) bool {

0 commit comments

Comments
 (0)