Как изменяется емкость слайса при расширении в Go
Что проверяют
Знаете ли вы, что рост не всегда двукратный, и понимаете ли, зачем задавать ёмкость заранее.
Как отвечать
Когда append не помещается в текущую ёмкость, рантайм выделяет новый массив, копирует данные и возвращает слайс, указывающий уже на него.
Правило роста менялось, и точные числа знать наизусть не требуется, но суть такая: маленькие слайсы растут примерно вдвое, а начиная с некоторого размера (порядка 256 элементов) коэффициент постепенно снижается к 1.25. Так сделано, чтобы большие слайсы не удваивались, съедая память скачком. Полученное число ещё округляется до размера класса аллокатора, поэтому фактическая ёмкость может оказаться больше расчётной.
Проверить это можно прямо в коде, печатая len и cap в цикле: видно, что ёмкость растёт ступенями, а не на единицу.
Практические следствия:
- знаете объём заранее, задавайте его:
make([]T, 0, n)избавляет от цепочки перевыделений и копирований. На больших объёмах разница в разы; - после роста слайс указывает на другой массив, поэтому результат
appendобязательно присваивают обратно, а старые подслайсы перестают быть связанными с новым; - обратная ситуация: если ёмкости хватило, запись идёт в тот же массив, и это видно всем, кто на него ссылается.
Где спотыкаются
Утверждают, что ёмкость всегда удваивается.
Не задают ёмкость, зная размер, и получают лишние копирования.
Забывают присвоить результат append.
Считают, что append всегда создаёт новый массив.