Есть два случая:
- Локальный срез: длина будет кэшироваться без накладных расходов.
- Глобальный срез или переданный (по ссылке): длина не может быть кэширована, и есть накладные расходы
Нет накладных расходов на локальные срезы
Для локально определенных срезов длина кэшируется, поэтому нет дополнительных затрат времени выполнения. Вы можете увидеть это в сборке следующей программы:
func generateSlice(x int) []int {
return make([]int, x)
}
func main() {
x := generateSlice(10)
println(len(x))
}
В компиляции с go tool 6g -S test.go
это дает, среди прочего, следующие строки:
MOVQ "".x+40(SP),BX
MOVQ BX,(SP)
// ...
CALL ,runtime.printint(SB)
Здесь происходит то, что первая строка извлекает длину x
, получая значение, расположенное в 40 байтах от начала x
, и, что наиболее важно, кэширует это значение в BX
, которое затем используется для каждого вхождения len(x)
. Причина смещения в том, что массив имеет следующую структуру (источник):
typedef struct
{ // must not move anything
uchar array[8]; // pointer to data
uchar nel[4]; // number of elements
uchar cap[4]; // allocated number of elements
} Array;
nel
— это то, к чему обращается len()
. Вы можете увидеть это в генерация кода.
Глобальные и ссылочные срезы имеют накладные расходы
Для общих значений кэширование длины невозможно, так как компилятор должен предположить, что срез изменяется между вызовами. Поэтому компилятору приходится каждый раз писать код, который напрямую обращается к атрибуту длины. Пример:
func accessLocal() int {
a := make([]int, 1000) // local
count := 0
for i := 0; i < len(a); i++ {
count += len(a)
}
return count
}
var ag = make([]int, 1000) // pseudo-code
func accessGlobal() int {
count := 0
for i := 0; i < len(ag); i++ {
count += len(ag)
}
return count
}
Сравнение сборки обеих функций дает ключевую разницу в том, что, как только переменная становится глобальной, доступ к атрибуту nel
больше не кэшируется, и будут накладные расходы времени выполнения:
// accessLocal
MOVQ "".a+8048(SP),SI // cache length in SI
// ...
CMPQ SI,AX // i < len(a)
// ...
MOVQ SI,BX
ADDQ CX,BX
MOVQ BX,CX // count += len(a)
// accessGlobal
MOVQ "".ag+8(SB),BX
CMPQ BX,AX // i < len(ag)
// ...
MOVQ "".ag+8(SB),BX
ADDQ CX,BX
MOVQ BX,CX // count += len(ag)
person
nemo
schedule
29.10.2014