perf: reduce memory allocations in request generator

This commit is contained in:
2026-03-22 22:26:03 +04:00
parent 9215fd8767
commit 44c35e6681
2 changed files with 48 additions and 31 deletions

View File

@@ -43,19 +43,34 @@ func NewRequestGenerator(
randSource := NewDefaultRandSource() randSource := NewDefaultRandSource()
//nolint:gosec // G404: Using non-cryptographic rand for load testing, not security //nolint:gosec // G404: Using non-cryptographic rand for load testing, not security
localRand := rand.New(randSource) localRand := rand.New(randSource)
templateFuncMap := NewDefaultTemplateFuncMap(randSource, fileCache)
pathGenerator, isPathGeneratorDynamic := createTemplateFunc(requestURL.Path, templateFuncMap) // Funcs() is only called if a value actually contains template syntax.
methodGenerator, isMethodGeneratorDynamic := NewMethodGeneratorFunc(localRand, methods, templateFuncMap) // The root template is shared across all createTemplateFunc calls so Funcs() is called at most once.
paramsGenerator, isParamsGeneratorDynamic := NewParamsGeneratorFunc(localRand, params, templateFuncMap) var templateRoot *template.Template
headersGenerator, isHeadersGeneratorDynamic := NewHeadersGeneratorFunc(localRand, headers, templateFuncMap) lazyTemplateRoot := func() *template.Template {
cookiesGenerator, isCookiesGeneratorDynamic := NewCookiesGeneratorFunc(localRand, cookies, templateFuncMap) if templateRoot == nil {
templateRoot = template.New("").Funcs(NewDefaultTemplateFuncMap(randSource, fileCache))
}
return templateRoot
}
pathGenerator, isPathGeneratorDynamic := createTemplateFunc(requestURL.Path, lazyTemplateRoot)
methodGenerator, isMethodGeneratorDynamic := NewMethodGeneratorFunc(localRand, methods, lazyTemplateRoot)
paramsGenerator, isParamsGeneratorDynamic := NewParamsGeneratorFunc(localRand, params, lazyTemplateRoot)
headersGenerator, isHeadersGeneratorDynamic := NewHeadersGeneratorFunc(localRand, headers, lazyTemplateRoot)
cookiesGenerator, isCookiesGeneratorDynamic := NewCookiesGeneratorFunc(localRand, cookies, lazyTemplateRoot)
bodyTemplateFuncMapData := &BodyTemplateFuncMapData{} bodyTemplateFuncMapData := &BodyTemplateFuncMapData{}
bodyTemplateFuncMap := NewDefaultBodyTemplateFuncMap(randSource, bodyTemplateFuncMapData, fileCache) var bodyTemplateRoot *template.Template
bodyGenerator, isBodyGeneratorDynamic := NewBodyGeneratorFunc(localRand, bodies, bodyTemplateFuncMap) lazyBodyTemplateRoot := func() *template.Template {
if bodyTemplateRoot == nil {
bodyTemplateRoot = template.New("").Funcs(NewDefaultBodyTemplateFuncMap(randSource, bodyTemplateFuncMapData, fileCache))
}
return bodyTemplateRoot
}
bodyGenerator, isBodyGeneratorDynamic := NewBodyGeneratorFunc(localRand, bodies, lazyBodyTemplateRoot)
valuesGenerator := NewValuesGeneratorFunc(values, templateFuncMap) valuesGenerator := NewValuesGeneratorFunc(values, lazyTemplateRoot)
hasScripts := scriptTransformer != nil && !scriptTransformer.IsEmpty() hasScripts := scriptTransformer != nil && !scriptTransformer.IsEmpty()
@@ -170,8 +185,8 @@ func applyRequestDataToFastHTTP(reqData *script.RequestData, req *fasthttp.Reque
} }
} }
func NewMethodGeneratorFunc(localRand *rand.Rand, methods []string, templateFunctions template.FuncMap) (requestDataGenerator, bool) { func NewMethodGeneratorFunc(localRand *rand.Rand, methods []string, lazyRoot func() *template.Template) (requestDataGenerator, bool) {
methodGenerator, isDynamic := buildStringSliceGenerator(localRand, methods, templateFunctions) methodGenerator, isDynamic := buildStringSliceGenerator(localRand, methods, lazyRoot)
var ( var (
method string method string
@@ -188,8 +203,8 @@ func NewMethodGeneratorFunc(localRand *rand.Rand, methods []string, templateFunc
}, isDynamic }, isDynamic
} }
func NewBodyGeneratorFunc(localRand *rand.Rand, bodies []string, templateFunctions template.FuncMap) (requestDataGenerator, bool) { func NewBodyGeneratorFunc(localRand *rand.Rand, bodies []string, lazyRoot func() *template.Template) (requestDataGenerator, bool) {
bodyGenerator, isDynamic := buildStringSliceGenerator(localRand, bodies, templateFunctions) bodyGenerator, isDynamic := buildStringSliceGenerator(localRand, bodies, lazyRoot)
var ( var (
body string body string
@@ -206,8 +221,8 @@ func NewBodyGeneratorFunc(localRand *rand.Rand, bodies []string, templateFunctio
}, isDynamic }, isDynamic
} }
func NewParamsGeneratorFunc(localRand *rand.Rand, params types.Params, templateFunctions template.FuncMap) (requestDataGenerator, bool) { func NewParamsGeneratorFunc(localRand *rand.Rand, params types.Params, lazyRoot func() *template.Template) (requestDataGenerator, bool) {
generators, isDynamic := buildKeyValueGenerators(localRand, params, templateFunctions) generators, isDynamic := buildKeyValueGenerators(localRand, params, lazyRoot)
var ( var (
key, value string key, value string
@@ -231,8 +246,8 @@ func NewParamsGeneratorFunc(localRand *rand.Rand, params types.Params, templateF
}, isDynamic }, isDynamic
} }
func NewHeadersGeneratorFunc(localRand *rand.Rand, headers types.Headers, templateFunctions template.FuncMap) (requestDataGenerator, bool) { func NewHeadersGeneratorFunc(localRand *rand.Rand, headers types.Headers, lazyRoot func() *template.Template) (requestDataGenerator, bool) {
generators, isDynamic := buildKeyValueGenerators(localRand, headers, templateFunctions) generators, isDynamic := buildKeyValueGenerators(localRand, headers, lazyRoot)
var ( var (
key, value string key, value string
@@ -256,8 +271,8 @@ func NewHeadersGeneratorFunc(localRand *rand.Rand, headers types.Headers, templa
}, isDynamic }, isDynamic
} }
func NewCookiesGeneratorFunc(localRand *rand.Rand, cookies types.Cookies, templateFunctions template.FuncMap) (requestDataGenerator, bool) { func NewCookiesGeneratorFunc(localRand *rand.Rand, cookies types.Cookies, lazyRoot func() *template.Template) (requestDataGenerator, bool) {
generators, isDynamic := buildKeyValueGenerators(localRand, cookies, templateFunctions) generators, isDynamic := buildKeyValueGenerators(localRand, cookies, lazyRoot)
var ( var (
key, value string key, value string
@@ -281,11 +296,11 @@ func NewCookiesGeneratorFunc(localRand *rand.Rand, cookies types.Cookies, templa
}, isDynamic }, isDynamic
} }
func NewValuesGeneratorFunc(values []string, templateFunctions template.FuncMap) func() (valuesData, error) { func NewValuesGeneratorFunc(values []string, lazyRoot func() *template.Template) func() (valuesData, error) {
generators := make([]func(_ any) (string, error), len(values)) generators := make([]func(_ any) (string, error), len(values))
for i, v := range values { for i, v := range values {
generators[i], _ = createTemplateFunc(v, templateFunctions) generators[i], _ = createTemplateFunc(v, lazyRoot)
} }
var ( var (
@@ -313,8 +328,12 @@ func NewValuesGeneratorFunc(values []string, templateFunctions template.FuncMap)
} }
} }
func createTemplateFunc(value string, templateFunctions template.FuncMap) (func(data any) (string, error), bool) { func createTemplateFunc(value string, lazyRoot func() *template.Template) (func(data any) (string, error), bool) {
tmpl, err := template.New("").Funcs(templateFunctions).Parse(value) if !strings.Contains(value, "{{") {
return func(_ any) (string, error) { return value, nil }, false
}
tmpl, err := lazyRoot().New("").Parse(value)
if err == nil && hasTemplateActions(tmpl) { if err == nil && hasTemplateActions(tmpl) {
var err error var err error
return func(data any) (string, error) { return func(data any) (string, error) {
@@ -340,7 +359,7 @@ type keyValueItem interface {
func buildKeyValueGenerators[T keyValueItem]( func buildKeyValueGenerators[T keyValueItem](
localRand *rand.Rand, localRand *rand.Rand,
items []T, items []T,
templateFunctions template.FuncMap, lazyRoot func() *template.Template,
) ([]keyValueGenerator, bool) { ) ([]keyValueGenerator, bool) {
isDynamic := false isDynamic := false
generators := make([]keyValueGenerator, len(items)) generators := make([]keyValueGenerator, len(items))
@@ -350,7 +369,7 @@ func buildKeyValueGenerators[T keyValueItem](
keyValue := types.KeyValue[string, []string](item) keyValue := types.KeyValue[string, []string](item)
// Generate key function // Generate key function
keyFunc, keyIsDynamic := createTemplateFunc(keyValue.Key, templateFunctions) keyFunc, keyIsDynamic := createTemplateFunc(keyValue.Key, lazyRoot)
if keyIsDynamic { if keyIsDynamic {
isDynamic = true isDynamic = true
} }
@@ -358,7 +377,7 @@ func buildKeyValueGenerators[T keyValueItem](
// Generate value functions // Generate value functions
valueFuncs := make([]func(data any) (string, error), len(keyValue.Value)) valueFuncs := make([]func(data any) (string, error), len(keyValue.Value))
for j, v := range keyValue.Value { for j, v := range keyValue.Value {
valueFunc, valueIsDynamic := createTemplateFunc(v, templateFunctions) valueFunc, valueIsDynamic := createTemplateFunc(v, lazyRoot)
if valueIsDynamic { if valueIsDynamic {
isDynamic = true isDynamic = true
} }
@@ -381,7 +400,7 @@ func buildKeyValueGenerators[T keyValueItem](
func buildStringSliceGenerator( func buildStringSliceGenerator(
localRand *rand.Rand, localRand *rand.Rand,
values []string, values []string,
templateFunctions template.FuncMap, lazyRoot func() *template.Template,
) (func() func(data any) (string, error), bool) { ) (func() func(data any) (string, error), bool) {
// Return a function that returns an empty string generator if values is empty // Return a function that returns an empty string generator if values is empty
if len(values) == 0 { if len(values) == 0 {
@@ -393,7 +412,7 @@ func buildStringSliceGenerator(
valueFuncs := make([]func(data any) (string, error), len(values)) valueFuncs := make([]func(data any) (string, error), len(values))
for i, value := range values { for i, value := range values {
valueFunc, valueIsDynamic := createTemplateFunc(value, templateFunctions) valueFunc, valueIsDynamic := createTemplateFunc(value, lazyRoot)
if valueIsDynamic { if valueIsDynamic {
isDynamic = true isDynamic = true
} }

View File

@@ -484,13 +484,11 @@ func newHostClients(
proxiesRaw[i] = url.URL(proxy) proxiesRaw[i] = url.URL(proxy)
} }
maxConns := max(fasthttp.DefaultMaxConnsPerHost, workers)
maxConns = ((maxConns * 50 / 100) + maxConns)
return NewHostClients( return NewHostClients(
ctx, ctx,
timeout, timeout,
proxiesRaw, proxiesRaw,
maxConns, workers,
requestURL, requestURL,
skipCertVerify, skipCertVerify,
) )