Discrepancy Between Calculated and Actual capacity in Slice

54 views Asked by At

I've been working on understanding Go's slice capacity behavior and I have come across something I find puzzling. I wrote a piece of code to observe the changes in the slice's capacity as elements were added to it using the append() function. Here is my code that reproduces the issue:

package main

import (
    "testing"
)

func TestCap(t *testing.T) {
    var s []int
    lastCap := cap(s)
    
    for i := 0; i < 1000; i++ {
        s = append(s, i)
        newCap := cap(s)
        
        if newCap != lastCap {
            expectedCap := calculateExpectedCap(lastCap, i+1)
            t.Logf("Added element %d, length: %d, actual capacity: %d, expected capacity: %d\n", 
            i, len(s), newCap, expectedCap)
            lastCap = newCap
        }
    }
}

func calculateExpectedCap(oldcap, cap int) int {
    threshold := 256
    if cap > oldcap*2 {
        return cap
    }
    if oldcap < threshold {
        return oldcap * 2
    }
    return oldcap + (oldcap+3*threshold)/4
}

Output:

=== RUN   TestCap
    cap_test.go:17: Added element 0, length: 1, actual capacity: 1, expected capacity: 1
    cap_test.go:17: Added element 1, length: 2, actual capacity: 2, expected capacity: 2
    cap_test.go:17: Added element 2, length: 3, actual capacity: 4, expected capacity: 4
    cap_test.go:17: Added element 4, length: 5, actual capacity: 8, expected capacity: 8
    cap_test.go:17: Added element 8, length: 9, actual capacity: 16, expected capacity: 16
    cap_test.go:17: Added element 16, length: 17, actual capacity: 32, expected capacity: 32
    cap_test.go:17: Added element 32, length: 33, actual capacity: 64, expected capacity: 64
    cap_test.go:17: Added element 64, length: 65, actual capacity: 128, expected capacity: 128
    cap_test.go:17: Added element 128, length: 129, actual capacity: 256, expected capacity: 256
    cap_test.go:17: Added element 256, length: 257, actual capacity: 512, expected capacity: 512
    cap_test.go:17: Added element 512, length: 513, actual capacity: 848, expected capacity: 832
    cap_test.go:17: Added element 848, length: 849, actual capacity: 1280, expected capacity: 1252
--- PASS: TestCap (0.00s)
PASS

Most of the time, the actual capacity is as expected. However, when the length of the slice is 513, my expected capacity based on Go's capacity expansion rules is 832, but the program records it as 848. Again, at length 849, the expected capacity is 1252, while the actual capacity turns out to be 1280. I am working with go1.21.5 (darwin/arm64) and was wondering if this discrepancy is due to some idiosyncrasy in this particular version of Golang or is it a feature in Golang's actual capacity allocation which I'm potentially misunderstanding? Can anyone provide an explanation for this inconsistency?

0

There are 0 answers