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

engine: page defragmentation #202

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/engine/storage/page/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
const (
// Size is the fix size of a page, which is 16KB or 16384 bytes.
Size = 1 << 14
// HeaderSize is the fix size of a page header, which is 10 bytes.
// HeaderSize is the fix size of a page header, which is 6 bytes.
HeaderSize = 6
)

Expand Down
63 changes: 63 additions & 0 deletions internal/engine/storage/page/page_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package page
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please read the contribution and coding guidelines.


import (
"math"
"math/rand"
"testing"
)

const (
maxKeySize = 100
maxRecordSize = 1_000
)

var (
pageSize = int(math.Round(2 * float64(Size) / 3)) // two-thirds of the max size
)

func generateRecordCell(b *testing.B) RecordCell {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ordering: exported before unexported

keySize := 1 + rand.Intn(maxKeySize-1)
recordSize := 1 + rand.Intn(maxRecordSize-1)
Comment on lines +19 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use an explicitely seeded random source, to be sure that the test is always reproducible, regardless of someone else seeding the whole rand package or not.

key, record := make([]byte, keySize), make([]byte, recordSize)
_, err := rand.Read(key)
if err != nil {
b.Fatal(err)
}
Comment on lines +23 to +25
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We tend to use stretchr/testify for tests.
You could easily rewrite this as

assert := assert.New(b)
...
_, err := rand.Read(key)
assert.NoError(err)
...

And you would get rid of a lot of err checks like this.

_, err = rand.Read(record)
if err != nil {
b.Fatal(err)
}

return RecordCell{
Key: key,
Record: record,
}
}

func generatePage(b *testing.B) *Page {
b.Helper()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a helper, but generateCell isn't?

p, err := load(make([]byte, pageSize))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only use a page with 2 thirds of the total size? Is that benchmark still representative if the page is smaller than 16K?

if err != nil {
b.Fatal(err)
}
record := generateRecordCell(b)
for err = p.StoreRecordCell(record); err != ErrPageFull; err = p.StoreRecordCell(record) {
record = generateRecordCell(b)
}
Comment on lines +43 to +46
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
record := generateRecordCell(b)
for err = p.StoreRecordCell(record); err != ErrPageFull; err = p.StoreRecordCell(record) {
record = generateRecordCell(b)
}
for {
rec := generateRecordCell(b)
if err := p.StoreRecordCell(rec); err != nil {
assert.Equal(ErrPageFull, err)
break
}
}

return p
}

func BenchmarkPage_Defragment(b *testing.B) {
data := generatePage(b).data
bytes := make([]byte, len(data))
b.ResetTimer()
b.ReportAllocs()

for i := 0; i < b.N; i++ {
copy(bytes, data)
Comment on lines +51 to +57
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

page.RawData() does exactly that, return a copy of the page data. That way, you don't need to access unexported fields.

page := &Page{ // have to recreate a page with initial data
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Load rather than this. This does work, however, if the page implementation changes, this probably doesn't work any more (e.g. adding locks or cell caches as maps etc)

data: bytes,
}
Comment on lines +57 to +60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably not be part of the benchmark, exclude it with b.StopTimer() and b.StartTimer() respectively

page.Defragment()
}
}