Что такое механизм эвакуации в хэш-таблицах (map) в Go?

Ответ

Механизм эвакуации (evacuation) в Go — это процесс постепенного переноса данных из старой хэш-таблицы в новую, увеличенную, когда map становится слишком заполненной.

Когда это происходит?
Эвакуация начинается, когда коэффициент загрузки (load factor) карты превышает 6.5. Это означает, что в среднем на одну корзину (bucket) приходится более 6.5 элементов.

Как это работает?

  1. Создание новой таблицы: Go выделяет память под новую хэш-таблицу, которая обычно в два раза больше старой.
  2. Постепенный перенос: Вместо того чтобы остановить все операции и скопировать все данные сразу (что вызвало бы заметную задержку), Go переносит данные инкрементально. При каждой операции записи или удаления в map эвакуируются (переносятся) одна или две старые корзины.
  3. Двойное чтение: Во время эвакуации, если ключ не найден в новой таблице, Go ищет его в старой. Это гарантирует, что все данные остаются доступными в течение всего процесса.

Зачем это нужно?
Основная цель — избежать больших пауз (stop-the-world) при изменении размера map. Постепенная эвакуация "размазывает" затраты на перераспределение по множеству мелких операций, что критически важно для систем с низкими задержками (low-latency).

Пример, который инициирует эвакуацию:

package main

import "fmt"

func main() {
    // Создаем карту с начальной емкостью 8 корзин (buckets)
    m := make(map[int]string)

    // Добавляем большое количество элементов
    // По мере заполнения карты коэффициент загрузки будет расти,
    // и Go запустит механизм эвакуации для её расширения.
    for i := 0; i < 1000; i++ {
        m[i] = fmt.Sprintf("value_%d", i)
    }
    fmt.Println("Map populated, evacuation occurred.")
}