From 9d05c1fbbfd1578e8a13f1cb772ba1fd16536d95 Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:12:15 +0530 Subject: [PATCH 01/15] Reduce heap allocation #1 --- internal/corazarules/rule_match.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/corazarules/rule_match.go b/internal/corazarules/rule_match.go index 67f8f7dc1..5b6700ed4 100644 --- a/internal/corazarules/rule_match.go +++ b/internal/corazarules/rule_match.go @@ -33,27 +33,27 @@ type MatchData struct { var _ types.MatchData = (*MatchData)(nil) -func (m *MatchData) Variable() variables.RuleVariable { +func (m MatchData) Variable() variables.RuleVariable { return m.Variable_ } -func (m *MatchData) Key() string { +func (m MatchData) Key() string { return m.Key_ } -func (m *MatchData) Value() string { +func (m MatchData) Value() string { return m.Value_ } -func (m *MatchData) Message() string { +func (m MatchData) Message() string { return m.Message_ } -func (m *MatchData) Data() string { +func (m MatchData) Data() string { return m.Data_ } -func (m *MatchData) ChainLevel() int { +func (m MatchData) ChainLevel() int { return m.ChainLevel_ } From 6c15ec9227ffab2d54d25d5ae742cd322561c69c Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:54:33 +0530 Subject: [PATCH 02/15] Remove append, use simple allocation --- internal/collections/map.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/collections/map.go b/internal/collections/map.go index 069c8e6a9..e2c83104c 100644 --- a/internal/collections/map.go +++ b/internal/collections/map.go @@ -122,14 +122,14 @@ func (c *Map) Add(key string, value string) { // Set sets the value of a key with the array of strings passed. If the key already exists, it will be overwritten. func (c *Map) Set(key string, values []string) { - originalKey := key if !c.isCaseSensitive { key = strings.ToLower(key) } - c.data[key] = make([]keyValue, 0, len(values)) - for _, v := range values { - c.data[key] = append(c.data[key], keyValue{key: originalKey, value: v}) + dataSlice := make([]keyValue, len(values)) + for i, v := range values { + dataSlice[i] = keyValue{key: key, value: v} } + c.data[key] = dataSlice } // SetIndex sets the value of a key at the specified index. If the key already exists, it will be overwritten. From 8ee2395ab4412a1fbe9eed42a72009746326fec1 Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:22:03 +0530 Subject: [PATCH 03/15] Remove append, use simple allocation --- internal/collections/map.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/internal/collections/map.go b/internal/collections/map.go index e2c83104c..5ae16b4ec 100644 --- a/internal/collections/map.go +++ b/internal/collections/map.go @@ -41,17 +41,18 @@ func NewCaseSensitiveKeyMap(variable variables.RuleVariable) *Map { } func (c *Map) Get(key string) []string { - if len(c.data) == 0 { - return nil - } if !c.isCaseSensitive { key = strings.ToLower(key) } - var values []string - for _, a := range c.data[key] { - values = append(values, a.value) + values := c.data[key] + if len(values) == 0 { + return nil + } + result := make([]string, 0, len(values)) + for _, a := range values { + result = append(result, a.value) } - return values + return result } // FindRegex returns all map elements whose key matches the regular expression. From fd6c77cab6096b5bf6c5457cf000a2a2f1422e0b Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:29:13 +0530 Subject: [PATCH 04/15] Add benchmark + Get --- internal/collections/map_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/internal/collections/map_test.go b/internal/collections/map_test.go index 68bb83c42..d7d1a38eb 100644 --- a/internal/collections/map_test.go +++ b/internal/collections/map_test.go @@ -16,6 +16,7 @@ package collections import ( "fmt" "regexp" + "strconv" "testing" "github.com/corazawaf/coraza/v3/types/variables" @@ -106,3 +107,14 @@ func TestNewCaseSensitiveKeyMap(t *testing.T) { } } + +func BenchmarkTxGet(b *testing.B) { + c := NewCaseSensitiveKeyMap(variables.RequestHeaders) + for i := 0; i < b.N; i++ { + c.Set("key"+strconv.Itoa(i), []string{"value1", "value2"}) + } + // Benchmark the Get method + for i := 0; i < b.N; i++ { + c.Get("key500") + } +} From eb95e4298178151fe176683c820ee854a929f3fd Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:40:35 +0530 Subject: [PATCH 05/15] Improve test --- internal/collections/map_test.go | 36 ++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/internal/collections/map_test.go b/internal/collections/map_test.go index d7d1a38eb..a51d429b4 100644 --- a/internal/collections/map_test.go +++ b/internal/collections/map_test.go @@ -16,7 +16,6 @@ package collections import ( "fmt" "regexp" - "strconv" "testing" "github.com/corazawaf/coraza/v3/types/variables" @@ -108,13 +107,32 @@ func TestNewCaseSensitiveKeyMap(t *testing.T) { } -func BenchmarkTxGet(b *testing.B) { +func BenchmarkTxSetGet(b *testing.B) { + // Set up the map once outside of the loop c := NewCaseSensitiveKeyMap(variables.RequestHeaders) - for i := 0; i < b.N; i++ { - c.Set("key"+strconv.Itoa(i), []string{"value1", "value2"}) - } - // Benchmark the Get method - for i := 0; i < b.N; i++ { - c.Get("key500") - } + + // Benchmark the Set operation + b.Run("Set", func(b *testing.B) { + for i := 0; i < b.N; i++ { + key := fmt.Sprintf("key%d", i) + c.Set(key, []string{"value2"}) + } + }) + + // Benchmark the Get operation + b.Run("Get", func(b *testing.B) { + // Ensure some values are already set for Get to work + for i := 0; i < b.N; i++ { + key := fmt.Sprintf("key%d", i) + c.Set(key, []string{"value2"}) + } + + for i := 0; i < b.N; i++ { + key := fmt.Sprintf("key%d", i) + c.Get(key) + } + }) + + // Report all memory allocations during the benchmark + b.ReportAllocs() } From 318a4f327ed3a40f8b56b0f29c04e26ce76db9be Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:49:00 +0530 Subject: [PATCH 06/15] Minor improvments --- internal/collections/named.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/collections/named.go b/internal/collections/named.go index 88d9166fd..855566750 100644 --- a/internal/collections/named.go +++ b/internal/collections/named.go @@ -61,11 +61,11 @@ func (c *NamedCollection) Len() int { // Data is an internal method used for serializing to JSON func (c *NamedCollection) Data() map[string][]string { - result := map[string][]string{} + result := make(map[string][]string, len(c.data)) for k, v := range c.data { - result[k] = make([]string, 0, len(v)) - for _, a := range v { - result[k] = append(result[k], a.value) + result[k] = make([]string, len(v)) + for i, a := range v { + result[k][i] = a.value } } return result From ad61ff4af18956525594a9292e88173f6e04957d Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:32:29 +0530 Subject: [PATCH 07/15] Minor fix --- internal/collections/map_test.go | 7 ------- internal/operators/rx_test.go | 10 ++++++++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/internal/collections/map_test.go b/internal/collections/map_test.go index a51d429b4..9bc4f1fcd 100644 --- a/internal/collections/map_test.go +++ b/internal/collections/map_test.go @@ -108,20 +108,15 @@ func TestNewCaseSensitiveKeyMap(t *testing.T) { } func BenchmarkTxSetGet(b *testing.B) { - // Set up the map once outside of the loop c := NewCaseSensitiveKeyMap(variables.RequestHeaders) - - // Benchmark the Set operation b.Run("Set", func(b *testing.B) { for i := 0; i < b.N; i++ { key := fmt.Sprintf("key%d", i) c.Set(key, []string{"value2"}) } }) - // Benchmark the Get operation b.Run("Get", func(b *testing.B) { - // Ensure some values are already set for Get to work for i := 0; i < b.N; i++ { key := fmt.Sprintf("key%d", i) c.Set(key, []string{"value2"}) @@ -132,7 +127,5 @@ func BenchmarkTxSetGet(b *testing.B) { c.Get(key) } }) - - // Report all memory allocations during the benchmark b.ReportAllocs() } diff --git a/internal/operators/rx_test.go b/internal/operators/rx_test.go index e9785713b..580c30608 100644 --- a/internal/operators/rx_test.go +++ b/internal/operators/rx_test.go @@ -119,3 +119,13 @@ func BenchmarkRxSubstringVsMatch(b *testing.B) { rx.FindAllString(str, 3) }) } + +// Benchmark tests for both the original and optimized versions +func BenchmarkMatchesArbitraryBytesOriginal(b *testing.B) { + expr := "\\x41\\x42\\x43\\x44" // Test with a few escape sequences + b.ResetTimer() + + for i := 0; i < b.N; i++ { + matchesArbitraryBytes(expr) + } +} From e79bfec53a4ec6b79fcf9b69af4c5e5742f8c25e Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:34:14 +0530 Subject: [PATCH 08/15] Minor fix --- internal/operators/rx_test.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/internal/operators/rx_test.go b/internal/operators/rx_test.go index 580c30608..e9785713b 100644 --- a/internal/operators/rx_test.go +++ b/internal/operators/rx_test.go @@ -119,13 +119,3 @@ func BenchmarkRxSubstringVsMatch(b *testing.B) { rx.FindAllString(str, 3) }) } - -// Benchmark tests for both the original and optimized versions -func BenchmarkMatchesArbitraryBytesOriginal(b *testing.B) { - expr := "\\x41\\x42\\x43\\x44" // Test with a few escape sequences - b.ResetTimer() - - for i := 0; i < b.N; i++ { - matchesArbitraryBytes(expr) - } -} From d5b0055cdc4db4e2d3562955d982d2ed59a800cb Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:34:06 +0530 Subject: [PATCH 09/15] Minor fix --- internal/collections/map_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/collections/map_test.go b/internal/collections/map_test.go index 9bc4f1fcd..97c695a29 100644 --- a/internal/collections/map_test.go +++ b/internal/collections/map_test.go @@ -108,23 +108,23 @@ func TestNewCaseSensitiveKeyMap(t *testing.T) { } func BenchmarkTxSetGet(b *testing.B) { + // Pre-generate keys in a map to avoid fmt.Sprintf overhead during benchmarking + keys := make(map[int]string, b.N) + for i := 0; i < b.N; i++ { + keys[i] = fmt.Sprintf("key%d", i) + } c := NewCaseSensitiveKeyMap(variables.RequestHeaders) b.Run("Set", func(b *testing.B) { for i := 0; i < b.N; i++ { - key := fmt.Sprintf("key%d", i) - c.Set(key, []string{"value2"}) + c.Set(keys[i], []string{"value2"}) } }) - // Benchmark the Get operation b.Run("Get", func(b *testing.B) { for i := 0; i < b.N; i++ { - key := fmt.Sprintf("key%d", i) - c.Set(key, []string{"value2"}) + c.Set(keys[i], []string{"value2"}) } - for i := 0; i < b.N; i++ { - key := fmt.Sprintf("key%d", i) - c.Get(key) + c.Get(keys[i]) } }) b.ReportAllocs() From 3ee9151cc77f016e568a25cb4ff6aa32c68cd24e Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:51:24 +0530 Subject: [PATCH 10/15] Minor fix --- internal/collections/map.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/collections/map.go b/internal/collections/map.go index 5ae16b4ec..496411abb 100644 --- a/internal/collections/map.go +++ b/internal/collections/map.go @@ -41,6 +41,9 @@ func NewCaseSensitiveKeyMap(variable variables.RuleVariable) *Map { } func (c *Map) Get(key string) []string { + if len(c.data) == 0 { + return nil + } if !c.isCaseSensitive { key = strings.ToLower(key) } From 9d85e2881a2a5cd99c2ca574d6847528662c10b5 Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:24:23 +0530 Subject: [PATCH 11/15] Minor fix --- internal/collections/map.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/collections/map.go b/internal/collections/map.go index 496411abb..a8d730ec2 100644 --- a/internal/collections/map.go +++ b/internal/collections/map.go @@ -126,12 +126,13 @@ func (c *Map) Add(key string, value string) { // Set sets the value of a key with the array of strings passed. If the key already exists, it will be overwritten. func (c *Map) Set(key string, values []string) { + originalKey := key if !c.isCaseSensitive { key = strings.ToLower(key) } dataSlice := make([]keyValue, len(values)) for i, v := range values { - dataSlice[i] = keyValue{key: key, value: v} + dataSlice[i] = keyValue{key: originalKey, value: v} } c.data[key] = dataSlice } From 506e89106308e1731f023d74071609de27f47557 Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:57:56 +0530 Subject: [PATCH 12/15] Minor fix - allocations --- internal/collections/map.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/internal/collections/map.go b/internal/collections/map.go index a8d730ec2..169d02ddb 100644 --- a/internal/collections/map.go +++ b/internal/collections/map.go @@ -124,13 +124,18 @@ func (c *Map) Add(key string, value string) { c.data[key] = append(c.data[key], aVal) } -// Set sets the value of a key with the array of strings passed. If the key already exists, it will be overwritten. +// Sets the value of a key with the array of strings passed. If the key already exists, it will be overwritten. func (c *Map) Set(key string, values []string) { originalKey := key if !c.isCaseSensitive { key = strings.ToLower(key) } - dataSlice := make([]keyValue, len(values)) + dataSlice, exists := c.data[key] + if !exists || cap(dataSlice) < len(values) { + dataSlice = make([]keyValue, len(values)) + } else { + dataSlice = dataSlice[:len(values)] // Reuse existing slice with the same length + } for i, v := range values { dataSlice[i] = keyValue{key: originalKey, value: v} } From 537ba83bbc30e8d765d1434e17df8a4b593f53a4 Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Thu, 7 Nov 2024 17:19:43 +0530 Subject: [PATCH 13/15] Minor fix - allocations --- internal/collections/map_test.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/collections/map_test.go b/internal/collections/map_test.go index 97c695a29..ee44a320e 100644 --- a/internal/collections/map_test.go +++ b/internal/collections/map_test.go @@ -108,21 +108,23 @@ func TestNewCaseSensitiveKeyMap(t *testing.T) { } func BenchmarkTxSetGet(b *testing.B) { - // Pre-generate keys in a map to avoid fmt.Sprintf overhead during benchmarking keys := make(map[int]string, b.N) for i := 0; i < b.N; i++ { keys[i] = fmt.Sprintf("key%d", i) } c := NewCaseSensitiveKeyMap(variables.RequestHeaders) + + for i := 0; i < b.N; i++ { + c.Set(keys[i], []string{"value2"}) + } b.Run("Set", func(b *testing.B) { + b.ResetTimer() for i := 0; i < b.N; i++ { c.Set(keys[i], []string{"value2"}) } }) b.Run("Get", func(b *testing.B) { - for i := 0; i < b.N; i++ { - c.Set(keys[i], []string{"value2"}) - } + b.ResetTimer() for i := 0; i < b.N; i++ { c.Get(keys[i]) } From 95fe46e2f2aac342b230a9368cfdcc64b2b907e2 Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Thu, 7 Nov 2024 17:26:27 +0530 Subject: [PATCH 14/15] Minor fix - allocations --- internal/collections/map_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/collections/map_test.go b/internal/collections/map_test.go index ee44a320e..7c47d29c5 100644 --- a/internal/collections/map_test.go +++ b/internal/collections/map_test.go @@ -114,9 +114,6 @@ func BenchmarkTxSetGet(b *testing.B) { } c := NewCaseSensitiveKeyMap(variables.RequestHeaders) - for i := 0; i < b.N; i++ { - c.Set(keys[i], []string{"value2"}) - } b.Run("Set", func(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { From 1041aba123cac3cf3afe06553ea07cbf759370b2 Mon Sep 17 00:00:00 2001 From: soujanyanmbri <54130357+soujanyanmbri@users.noreply.github.com> Date: Fri, 8 Nov 2024 06:31:20 +0530 Subject: [PATCH 15/15] Minor fix - remove append --- internal/collections/map.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/collections/map.go b/internal/collections/map.go index 169d02ddb..ba6adf35e 100644 --- a/internal/collections/map.go +++ b/internal/collections/map.go @@ -51,9 +51,9 @@ func (c *Map) Get(key string) []string { if len(values) == 0 { return nil } - result := make([]string, 0, len(values)) - for _, a := range values { - result = append(result, a.value) + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.value } return result }