Как работает встроенная функция len() в Go и какова её производительность?

Ответ

В Go len() — это не обычная функция, а встроенная конструкция языка, которая обрабатывается компилятором особым образом. Она возвращает длину для коллекций нескольких типов.

Производительность и сложность: O(1)

Вызов len() всегда выполняется за константное время O(1). Это возможно, потому что длина является неотъемлемой частью внутренней структуры данных и хранится в ней явно. len() просто читает это готовое значение.

  • Слайс (Slice): Структура слайса в памяти (slice header) содержит три поля: указатель на нижележащий массив, длину (len) и ёмкость (cap). len(mySlice) — это прямое чтение поля len.
    // Внутреннее представление слайса
    type sliceHeader struct {
        Data uintptr
        Len  int
        Cap  int
    }
  • Строка (String): Строка, как и слайс, имеет заголовок с указателем на данные и длиной. len(myString) читает это поле. Важно: len() возвращает количество байт, а не рун (символов).
  • Массив (Array): Длина массива является частью его типа и известна на этапе компиляции. len(myArray) — это, по сути, константа.
  • Мапа (Map): Внутренняя структура мапы содержит счётчик количества элементов. len(myMap) возвращает значение этого счётчика.
  • Канал (Channel): len(myChan) возвращает количество элементов, находящихся в данный момент в буфере канала.

Связь с CPU-кэшем

Прямой связи между len() и CPU-кэшем нет. Однако, как и любая операция чтения из памяти, её скорость косвенно зависит от кэша.

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

Ответ 18+ 🔞

Слушай, а вот этот len() в Go — это вообще не функция, в натуре! Это типа встроенная магия компилятора, которая просто берёт и смотрит в паспорт твоей коллекции. И самое охуенное — это O(1), то есть константа, моментально, на раз-два.

Почему так быстро? Да потому что длина — это неотъемлемая часть любой структуры, она там лежит готовая, как пропуск в хату. len() её не вычисляет, а просто тырит, как последний распиздяй.

  • Слайс: У него внутри, в этих его потаённых полях, прямо так и записано: «Привет, я Len, вот моя цифра». len() просто подглядывает.
    // Вот что у него внутри, представляешь?
    type sliceHeader struct {
        Data uintptr // Куда смотреть
        Len  int     // А вот и длина, ебать!
        Cap  int     // А это сколько влезет
    }
  • Строка: Точно такая же хитрая жопа с указателем и длиной. Только помни: len() считает байты, а не символы. Хочешь руны — это отдельная история, там уже utf8.RuneCountInString.
  • Массив: Да тут вообще смех: длина массива — это часть его типа. Компилятор её знает ещё до того, как ты штаны надел. len() — это просто подставить готовую циферку.
  • Мапа: Внутри у неё счётчик элементов тикает. len() — это глянуть на счётчик.
  • Канал: len() покажет, сколько сообщений в буфере лежит и ждёт, пока их кто-нибудь заберёт.

А при чём тут кэш процессора, спросишь? Да ни при чём, напрямую-то. Но если ты только что вовсю дрочил этот слайс, и его заголовок уже в быстром кэше L1 залёг — то чтение длины будет вообще мгновенным, как удар током. Ну а если данные далеко в оперативке — чуть дольше, но всё равно быстрее, чем ты «ёпта» скажешь.

Короче, len() — это супер-пупер быстрая штука. Не парься об этом вообще.