In go, стоит ли избегать создания фрагментов размера 0?

У меня есть программа, в которой я собираюсь сделать много-много срезов, некоторые из которых могут быть пустыми:

nb := something() // something might return 0
slices = append(slices, make([]int, nb))

Выделяет ли make([]int, 0) некоторую память и, таким образом, менее эффективно использует память, чем срез nil, хотя они имеют одинаковое поведение? На сколько?

Если да, то стоит ли делать тест, чтобы избежать бесполезных выделений, или затраты процессорного времени на тест не стоят экономии в памяти (или по какой-либо другой причине не делать этого)?

var sl slice
nb := something()
if nb > 0 {
    sl = make([]int, nb)
}
slices = append(slices, sl)

person Fabien    schedule 23.10.2015    source источник
comment
Я бы, наверное, попробовал измерить.   -  person biziclop    schedule 23.10.2015
comment
Да, но тонкие измерения, как правило, подвержены ошибкам, и я боюсь, что контекст моего теста может иметь побочные эффекты, которые не имеют ничего общего с основной проблемой, с которой я имею дело. Кроме того, я предпочитаю знать, почему происходит то, что я измеряю, чем просто наблюдать, как это происходит.   -  person Fabien    schedule 23.10.2015


Ответы (2)


Нет разницы в выделенной памяти между

var a []T // nil slice

а также

a := make([]T, 0) // zero-length, zero-capacity, non-nil slice

Разница заключается в содержании заголовка фрагмента. Во втором случае указатель слайса содержит некоторый фиксированный адрес, одинаковый для всех выделений размера 0.

Если этот код находится в критически важной для производительности части программы, разница имеет ... довольно большое значение. В первом случае вы обнуляете заголовок среза, во втором случае вы выполняете 3-4 вызова функций, некоторые проверки диапазона на ограничение и длину и т. д., прежде чем malloc вернет указатель на нулевое основание.

person chill    schedule 23.10.2015

Выделяет ли make([]int, 0) немного памяти

Да, он выделяет заголовок среза, но не резервный массив. Если заголовок фрагмента не выходит за пределы текущей области, он может быть выделен в стеке.

менее эффективная память, чем нулевой срез

С точки зрения используемой памяти они одинаковы.

стоит ли делать тест, чтобы избежать бесполезных аллокаций

В общем да, 3 или 4 инструкции, необходимые для сравнения int, ничто по сравнению с циклами, которые вам понадобятся для выделения и инициализации памяти.

person thwd    schedule 23.10.2015
comment
@CodingPickle Я был осторожен, чтобы не предлагать обратное. Почему отрицательный голос? - person thwd; 23.10.2015
comment
Первая часть: достаточно честно. Я отредактирую ответ, чтобы указать на ноль. Вторая часть: Спецификация Go не дает никаких гарантий относительно стека и кучи. В этом смысле значение int может прекрасно жить в куче. Передача по значению или указателю также не имеет к этому никакого отношения. Поскольку OP помещает заголовки своих слайсов в другой слайс, они скорее всего выделяются в куче. Если нет, то скопировал туда из стека. Во всяком случае, это детали реализации. - person thwd; 23.10.2015