mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2026-02-06 18:43:09 +08:00
`MergeMaps` accounts for 11.41% of allocs (13.8 GB) in clusterbomb mode. With 1,305 combinations per target, this function is called millions of times in the hot path. RCA: * Request generator calls `MergeMaps` with single arg on every payload combination, incurring variadic overhead. * Build request merges same maps multiple times per request. * `BuildPayloadFromOptions` recomputes static CLI options on every call. * Variables calls `MergeMaps` $$2×N$$ times per variable evaluation (once in loop, once in `evaluateVariableValue`) Changes: Core optimizations in maps.go: * Pre-size merged map to avoid rehashing (30-40% reduction) * Add `CopyMap` for efficient single-map copy without variadic overhead. * Add `MergeMapsInto` for in-place mutation when caller owns destination. Hot path fixes: * Replace `MergeMaps(r.currentPayloads)` with `CopyMap(r.currentPayloads)` to eliminates allocation on every combination iteration. * Pre-allocate combined map once, extend in-place during `ForEach` loop instead of creating new map per variable (eliminates $$2×N$$ allocations per request). Caching with concurrency safety: * Cache `BuildPayloadFromOptions` computation in `sync.Map` keyed by `types.Options` ptr, but return copy to prevent concurrent modification. * Cost: shallow copy of ~10-20 entries vs. full merge of vars + env (85-90% savings in typical case) * Clear cache in `closeInternal()` to prevent memory leaks when SDK instances are created or destroyed. Estimated impact: 40-60% reduction in `MergeMaps` allocations (5.5-8.3 GB savings from original 13.8 GB). Safe for concurrent execution and SDK usage with multiple instances. Signed-off-by: Dwi Siswanto <git@dw1.io>
54 lines
1.6 KiB
Go
54 lines
1.6 KiB
Go
package generators
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
|
)
|
|
|
|
// optionsPayloadMap caches the result of BuildPayloadFromOptions per options
|
|
// pointer. This supports multiple SDK instances with different options running
|
|
// concurrently.
|
|
var optionsPayloadMap sync.Map // map[*types.Options]map[string]interface{}
|
|
|
|
// BuildPayloadFromOptions returns a map with the payloads provided via CLI.
|
|
//
|
|
// The result is cached per options pointer since options don't change during a run.
|
|
// Returns a copy of the cached map to prevent concurrent modification issues.
|
|
// Safe for concurrent use with multiple SDK instances.
|
|
func BuildPayloadFromOptions(options *types.Options) map[string]interface{} {
|
|
if options == nil {
|
|
return make(map[string]interface{})
|
|
}
|
|
|
|
if cached, ok := optionsPayloadMap.Load(options); ok {
|
|
return CopyMap(cached.(map[string]interface{}))
|
|
}
|
|
|
|
m := make(map[string]interface{})
|
|
|
|
// merge with vars
|
|
if !options.Vars.IsEmpty() {
|
|
m = MergeMaps(m, options.Vars.AsMap())
|
|
}
|
|
|
|
// merge with env vars
|
|
if options.EnvironmentVariables {
|
|
m = MergeMaps(EnvVars(), m)
|
|
}
|
|
|
|
actual, _ := optionsPayloadMap.LoadOrStore(options, m)
|
|
|
|
// Return a copy to prevent concurrent writes to the cached map
|
|
return CopyMap(actual.(map[string]interface{}))
|
|
}
|
|
|
|
// ClearOptionsPayloadMap clears the cached options payload.
|
|
// SDK users should call this when disposing of a NucleiEngine instance
|
|
// to prevent memory leaks if creating many short-lived instances.
|
|
func ClearOptionsPayloadMap(options *types.Options) {
|
|
if options != nil {
|
|
optionsPayloadMap.Delete(options)
|
|
}
|
|
}
|