Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: optimize MemoryStorage.Compact method by improving memory allocation #267

Closed
wants to merge 2 commits into from

Conversation

1911860538
Copy link

Avoiding the use of append, and directly copying remaining entries to a new slice for better performance.

@k8s-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 1911860538
Once this PR has been reviewed and has the lgtm label, please assign spzala for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot
Copy link

Hi @1911860538. Thanks for your PR.

I'm waiting for a etcd-io member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@1911860538 1911860538 changed the title perf: optimize MemoryStorage.Compact method by improving memory alloc… perf: optimize MemoryStorage.Compact method by improving memory allocation Feb 15, 2025
@ahrtr
Copy link
Member

ahrtr commented Feb 17, 2025

/ok-to-test

@1911860538
Copy link
Author

1911860538 commented Feb 18, 2025

/ok-to-test

My code update appears to be leading to unexpected issues because the original logic only copied the Term and Index of the i-th element, whereas after my modification, it now copies all fields of the i-th element.
Perhaps the following code modification is more appropriate?

ents := make([]pb.Entry, uint64(len(ms.ents)) - i)
// Only copy Index and Term of the compact index entry (first entry).
// These fields are sufficient for log matching in the Raft algorithm,
// while Type and Data are not needed since the entry's command has
// already been applied to the state machine.
ents[0].Index = ms.ents[i].Index
ents[0].Term = ms.ents[i].Term
copy(ents[1:], ms.ents[i+1:])

@1911860538
Copy link
Author

/ok-to-test

My code update appears to be leading to unexpected issues because the original logic only copied the Term and Index of the i-th element, whereas after my modification, it now copies all fields of the i-th element. Perhaps the following code modification is more appropriate?

ents := make([]pb.Entry, uint64(len(ms.ents)) - i)
// Only copy Index and Term of the compact index entry (first entry).
// These fields are sufficient for log matching in the Raft algorithm,
// while Type and Data are not needed since the entry's command has
// already been applied to the state machine.
ents[0].Index = ms.ents[i].Index
ents[0].Term = ms.ents[i].Term
copy(ents[1:], ms.ents[i+1:])

The Benchmark results of my local code indicate that this code modification results in a slight performance boost, whereas the previous code changes showed no performance improvement.
Therefore, I am going to close this pull request.

package entry_compact

import (
	"bytes"
	"testing"
)

type Entry struct {
	Term  uint64
	Index uint64
	Type  int32
	Data  []byte
}

const (
	compactIndex = 100
	entriesLen   = 1000
)

func getEntries(length int) []Entry {
	src := make([]Entry, length)
	for i := range length {
		src[i] = Entry{
			Term:  uint64(i),
			Index: uint64(i),
			Type:  int32(i),
			Data:  []byte{'0'},
		}
	}
	return src
}

func copy1(src []Entry, index int) []Entry {
	dst := make([]Entry, 1, len(src)-index)
	dst[0].Term = src[0].Term
	dst[0].Index = src[0].Index
	dst = append(dst, src[index+1:]...)
	return dst
}

func copy2(src []Entry, index int) []Entry {
	dst := make([]Entry, len(src)-index)
	dst[0].Term = src[0].Term
	dst[0].Index = src[0].Index
	copy(dst[1:], src[index+1:])
	return dst
}

func copy3(src []Entry, index int) []Entry {
	dst := make([]Entry, len(src)-index)
	copy(dst, src[index:])
	return dst
}

func BenchmarkCopy1(b *testing.B) {
	src := getEntries(entriesLen)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		copy1(src, compactIndex)
	}
}

func BenchmarkCopy2(b *testing.B) {
	src := getEntries(entriesLen)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		copy2(src, compactIndex)
	}
}

func BenchmarkCopy3(b *testing.B) {
	src := getEntries(entriesLen)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		copy3(src, compactIndex)
	}
}

func TestSame(t *testing.T) {
	src := getEntries(entriesLen)
	c1 := copy1(src, compactIndex)
	c2 := copy2(src, compactIndex)
	if len(c1) != len(c2) {
		t.Fatal("copied slices are not the same length")
	}
	for i := range c1 {
		c1i := c1[i]
		c2i := c2[i]
		if c1i.Term != c2i.Term || c1i.Index != c2i.Index || c1i.Type != c2i.Type || bytes.Compare(c1i.Data, c2i.Data) != 0 {
			t.Fatal("copied items are not same")
		}
	}
}

Test output

=== RUN   TestSame
--- PASS: TestSame (0.00s)
PASS

Process finished with the exit code 0

Benchmark output

goos: darwin
goarch: arm64
pkg: test_code/entry_compact
cpu: Apple M3 Pro
BenchmarkCopy1
BenchmarkCopy1-11    	  436888	      2679 ns/op	   49152 B/op	       1 allocs/op
BenchmarkCopy2
BenchmarkCopy2-11    	  452683	      2652 ns/op	   49152 B/op	       1 allocs/op
BenchmarkCopy3
BenchmarkCopy3-11    	  446326	      2681 ns/op	   49152 B/op	       1 allocs/op
PASS

Process finished with the exit code 0

@1911860538 1911860538 closed this Feb 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants