Benchmarking Golang Slice Allocation
I was reading The Impact of Pre-allocating Slice Memory on Performance in Golang and was curious about the benchmark, so I wanted to try it too for Autograd.
Slice preallocation means we specify the len when creating the slice and it will create a slice with n-empty items on it. Then, to assign a value to the slice, we need to use the index. Example of prealloc a userIDs
userIDs := make([]uuid.UUID, len(assignments))
for i, assignment := range assignments {
userIDs[i] = assignment.AssignedBy
}
In Autograd, there is a page where the admin can see a list of assignments. This page typically contains 10-20 assignments with pagination. Then, I was curious if there is an impact if we preallocate for this slice of struct, since in the article, the writer didn’t explain the struct layout. In the struct I tested, it has 8 fields, and 3 of them are also structs. In my honest opinion, this struct can represent many backend service codes.
var asg = assignments.Assignment{
ID: uuid.New(),
Name: "Assignment 1",
Description: "Description 1",
DeadlineAt: time.Now(),
Assigner: assignments.Assigner{
ID: uuid.New(),
Name: "Assigner 1",
Active: true,
},
CaseInputFile: assignments.CaseFile{
ID: uuid.New(),
URL: "http://example.com",
Type: "input",
TimestampMetadata: core.TimestampMetadata{
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
DeletedAt: null.NewTime(time.Now(), false),
},
},
CaseOutputFile: assignments.CaseFile{
ID: uuid.New(),
URL: "http://example.com",
Type: "input",
TimestampMetadata: core.TimestampMetadata{
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
DeletedAt: null.NewTime(time.Now(), false),
},
},
TimestampMetadata: core.NewTimestampMeta(time.Time{}),
}
For the benchmark, I want to check for lengths of 10, 20, and 50 slices to represent a real scenario. The result is interesting, where the preallocation has a big performance boost of 1000x! You can check the benchmark here.
go test -benchmem -bench . github.com/fahmifan/autograd/pkg/core/assignments
goos: darwin
goarch: arm64
pkg: github.com/fahmifan/autograd/pkg/core/assignments
BenchmarkAssignmentNoPrealloc_10-8 363208 3327 ns/op 15808 B/op 5 allocs/op
BenchmarkAssignmentPrealloc_10-8 165713173 7.198 ns/op 0 B/op 0 allocs/op
BenchmarkAssignmentNoPrealloc_20-8 177516 6080 ns/op 32192 B/op 6 allocs/op
BenchmarkAssignmentPrealloc_20-8 96115022 12.32 ns/op 0 B/op 0 allocs/op
BenchmarkAssignmentNoPrealloc_50-8 99542 12601 ns/op 64960 B/op 7 allocs/op
BenchmarkAssignmentPrealloc_50-8 43292256 27.91 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/fahmifan/autograd/pkg/core/assignments 8.980s
So, it’s highly recommended to always preallocate a slice if we know the size ahead of time. The overhead is small, but the performance boost is significant.