From f2ea95a453f91f80f6cdfa87cd2430dcf9cbe1e2 Mon Sep 17 00:00:00 2001 From: Liudon Date: Thu, 9 Apr 2026 16:04:02 +0800 Subject: [PATCH] perf: use map lookup instead of slice scan in NewKey() and NewSection() --- bench_test.go | 34 ++++++++++++++++++++++++++++++++++ file.go | 2 +- helper.go | 24 ------------------------ helper_test.go | 27 --------------------------- section.go | 2 +- 5 files changed, 36 insertions(+), 53 deletions(-) delete mode 100644 helper.go delete mode 100644 helper_test.go diff --git a/bench_test.go b/bench_test.go index 9fd72f0..5ba8027 100644 --- a/bench_test.go +++ b/bench_test.go @@ -15,6 +15,7 @@ package ini import ( + "fmt" "testing" ) @@ -114,3 +115,36 @@ func Benchmark_Key_SetValue_VisSection(b *testing.B) { sec.Key("NAME").SetValue("10") } } + +func Benchmark_NewKey_With_N_Keys(b *testing.B) { + for _, n := range []int{10, 100, 500, 1000} { + b.Run(fmt.Sprintf("keys=%d", n), func(b *testing.B) { + f := Empty() + sec, _ := f.NewSection("bench") + for i := 0; i < n; i++ { + sec.NewKey(fmt.Sprintf("key%d", i), "val") + } + target := fmt.Sprintf("key%d", n-1) + b.ResetTimer() + for i := 0; i < b.N; i++ { + sec.NewKey(target, "newval") + } + }) + } +} + +func Benchmark_NewSection_With_N_Sections(b *testing.B) { + for _, n := range []int{10, 100, 500, 1000} { + b.Run(fmt.Sprintf("sections=%d", n), func(b *testing.B) { + f := Empty() + for i := 0; i < n; i++ { + f.NewSection(fmt.Sprintf("sec%d", i)) + } + target := fmt.Sprintf("sec%d", n-1) + b.ResetTimer() + for i := 0; i < b.N; i++ { + f.NewSection(target) + } + }) + } +} diff --git a/file.go b/file.go index f8b2240..2a37fa9 100644 --- a/file.go +++ b/file.go @@ -94,7 +94,7 @@ func (f *File) NewSection(name string) (*Section, error) { defer f.lock.Unlock() } - if !f.options.AllowNonUniqueSections && inSlice(name, f.sectionList) { + if !f.options.AllowNonUniqueSections && f.sections[name] != nil { return f.sections[name][0], nil } diff --git a/helper.go b/helper.go deleted file mode 100644 index f9d80a6..0000000 --- a/helper.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package ini - -func inSlice(str string, s []string) bool { - for _, v := range s { - if str == v { - return true - } - } - return false -} diff --git a/helper_test.go b/helper_test.go deleted file mode 100644 index 439ad3b..0000000 --- a/helper_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package ini - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestIsInSlice(t *testing.T) { - ss := []string{"a", "b", "c"} - assert.True(t, inSlice("a", ss)) - assert.False(t, inSlice("d", ss)) -} diff --git a/section.go b/section.go index a3615d8..f885e57 100644 --- a/section.go +++ b/section.go @@ -75,7 +75,7 @@ func (s *Section) NewKey(name, val string) (*Key, error) { defer s.f.lock.Unlock() } - if inSlice(name, s.keyList) { + if _, ok := s.keys[name]; ok { if s.f.options.AllowShadows { if err := s.keys[name].addShadow(val); err != nil { return nil, err