mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-03-17 20:47:50 +02:00
perf(changelog): improve changelog sort performance (#5161)
This commit removes the unnecessary slice copy in `sortEntries`, and replaces `sort.Slice` [^1] with the new `slices.SortFunc` [^2]. As recommended by the Go documentation, `slices.SortFunc` is generally faster because it uses generic, whereas `sort.Slice` relies on reflection, which incurs additional allocations The benchmark result from the newly added `Benchmark_sortEntries` show approximately a 64% performance improvement. Benchmark result: ``` │ old.txt │ new.txt │ │ sec/op │ sec/op vs base │ _sortEntries/asc-16 16.458µ ± 1% 5.958µ ± 1% -63.80% (p=0.000 n=10) _sortEntries/desc-16 17.675µ ± 1% 6.020µ ± 0% -65.94% (p=0.000 n=10) geomean 17.06µ 5.989µ -64.89% │ old.txt │ new.txt │ │ B/op │ B/op vs base │ _sortEntries/asc-16 3.164Ki ± 0% 1.164Ki ± 0% -63.21% (p=0.000 n=10) _sortEntries/desc-16 3.422Ki ± 0% 1.164Ki ± 0% -65.98% (p=0.000 n=10) geomean 3.290Ki 1.164Ki -64.62% │ old.txt │ new.txt │ │ allocs/op │ allocs/op vs base │ _sortEntries/asc-16 68.00 ± 0% 25.00 ± 0% -63.24% (p=0.000 n=10) _sortEntries/desc-16 72.00 ± 0% 25.00 ± 0% -65.28% (p=0.000 n=10) geomean 69.97 25.00 -64.27% ``` [^1]: https://pkg.go.dev/sort#Slice [^2]: https://pkg.go.dev/slices#SortFunc Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
This commit is contained in:
parent
d2469666b8
commit
b8aef100f2
@ -2,12 +2,13 @@
|
||||
package changelog
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/caarlos0/log"
|
||||
@ -211,7 +212,7 @@ func formatChangelog(ctx *context.Context, entries []string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(groups, groupSort(groups))
|
||||
slices.SortFunc(groups, groupSort)
|
||||
for _, group := range groups {
|
||||
if len(group.entries) > 0 {
|
||||
result = append(result, group.title)
|
||||
@ -221,10 +222,8 @@ func formatChangelog(ctx *context.Context, entries []string) (string, error) {
|
||||
return strings.Join(result, newLineFor(ctx)), nil
|
||||
}
|
||||
|
||||
func groupSort(groups []changelogGroup) func(i, j int) bool {
|
||||
return func(i, j int) bool {
|
||||
return groups[i].order < groups[j].order
|
||||
}
|
||||
func groupSort(i, j changelogGroup) int {
|
||||
return cmp.Compare(i.order, j.order)
|
||||
}
|
||||
|
||||
func filterAndPrefixItems(ss []string) []string {
|
||||
@ -306,17 +305,16 @@ func sortEntries(ctx *context.Context, entries []string) []string {
|
||||
if direction == "" {
|
||||
return entries
|
||||
}
|
||||
result := make([]string, len(entries))
|
||||
copy(result, entries)
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
imsg := extractCommitInfo(result[i])
|
||||
jmsg := extractCommitInfo(result[j])
|
||||
slices.SortFunc(entries, func(i, j string) int {
|
||||
imsg := extractCommitInfo(i)
|
||||
jmsg := extractCommitInfo(j)
|
||||
compareRes := strings.Compare(imsg, jmsg)
|
||||
if direction == "asc" {
|
||||
return strings.Compare(imsg, jmsg) < 0
|
||||
return compareRes
|
||||
}
|
||||
return strings.Compare(imsg, jmsg) > 0
|
||||
return -compareRes
|
||||
})
|
||||
return result
|
||||
return entries
|
||||
}
|
||||
|
||||
func keep(filter *regexp.Regexp, entries []string) (result []string) {
|
||||
|
@ -9,14 +9,13 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/goreleaser/goreleaser/v2/internal/client"
|
||||
"github.com/goreleaser/goreleaser/v2/internal/git"
|
||||
"github.com/goreleaser/goreleaser/v2/internal/testctx"
|
||||
"github.com/goreleaser/goreleaser/v2/internal/testlib"
|
||||
"github.com/goreleaser/goreleaser/v2/pkg/config"
|
||||
"github.com/goreleaser/goreleaser/v2/pkg/context"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDescription(t *testing.T) {
|
||||
@ -289,6 +288,33 @@ func TestChangelogSort(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_sortEntries(b *testing.B) {
|
||||
ctx := testctx.New()
|
||||
entries := []string{
|
||||
"added feature 1",
|
||||
"fixed bug 2",
|
||||
"ignored: whatever",
|
||||
"docs: whatever",
|
||||
"something about cArs we dont need",
|
||||
"feat: added that thing",
|
||||
"Merge pull request #999 from goreleaser/some-branch",
|
||||
"this is not a Merge pull request",
|
||||
}
|
||||
|
||||
b.Run("asc", func(b *testing.B) {
|
||||
ctx.Config.Changelog.Sort = "asc"
|
||||
for range b.N {
|
||||
sortEntries(ctx, entries)
|
||||
}
|
||||
})
|
||||
b.Run("desc", func(b *testing.B) {
|
||||
ctx.Config.Changelog.Sort = "desc"
|
||||
for range b.N {
|
||||
sortEntries(ctx, entries)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestChangelogInvalidSort(t *testing.T) {
|
||||
ctx := testctx.NewWithCfg(config.Project{
|
||||
Changelog: config.Changelog{
|
||||
@ -814,12 +840,23 @@ func TestGroup(t *testing.T) {
|
||||
},
|
||||
}, testctx.WithCurrentTag("v0.0.2"), withFirstCommit(t))
|
||||
require.NoError(t, Pipe{}.Run(ctx))
|
||||
require.Contains(t, ctx.ReleaseNotes, "## Changelog")
|
||||
require.Contains(t, ctx.ReleaseNotes, "### Bots")
|
||||
require.Contains(t, ctx.ReleaseNotes, "### Features")
|
||||
require.Contains(t, ctx.ReleaseNotes, "### Bug Fixes")
|
||||
require.NotContains(t, ctx.ReleaseNotes, "### Catch nothing")
|
||||
require.Contains(t, ctx.ReleaseNotes, "### Others")
|
||||
require.Regexp(t, `## Changelog
|
||||
### Features
|
||||
\* \w+ feat: added that thing
|
||||
### Bug Fixes
|
||||
\* \w+ bug: Merge pull request #999 from goreleaser\/some-branch
|
||||
### Bots
|
||||
\* \w+ feat\(deps\): update foobar \[bot\]
|
||||
### Others
|
||||
\* \w+ first
|
||||
\* \w+ this is not a Merge pull request
|
||||
\* \w+ chore: something about cArs we dont need
|
||||
\* \w+ docs: whatever
|
||||
\* \w+ fix: whatever
|
||||
\* \w+ ignored: whatever
|
||||
\* \w+ fixed bug 2
|
||||
\* \w+ added feature 1
|
||||
`, ctx.ReleaseNotes)
|
||||
}
|
||||
|
||||
func TestGroupBadRegex(t *testing.T) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user