У Go Go нет реального способа уменьшить кусочек? Это проблема?

Я пробовал пойти в течение некоторого времени, и этот вопрос продолжает прослушивать меня. Скажем, я создаю несколько большой набор данных на кусочке (скажем, 10 миллионов int64).

package main

import (
    "math"
    "fmt"
)

func main() {
    var a []int64
    var i int64;
    upto := int64(math.Pow10(7))
    for i = 0; i < upto; i++ {
        a = append(a, i)
    }
    fmt.Println(cap(a))
}

Но потом я решил, что не хочу, чтобы большинство из них было так, что я хочу, чтобы в итоге было всего лишь 10 из них. Я пробовал и нарезать, и удалять методы в Go wiki, но ни один из них, похоже, не уменьшает емкость среза.

Итак, мой вопрос: у Go нет реального способа уменьшить емкость среза, которая была бы похожа на realloc() -ing с аргументом меньшего размера, чем в вашем предыдущем вызове на том же указателе в C? Это проблема и как с ней бороться?

Ответы

Ответ 1

Чтобы выполнить, по сути, realloc среза:

a = append([]T(nil), a[:newSize]...) // Thanks to @Dijkstra for pointing out the missing ellipsis.

Если он копирует элементы newSize в новое место памяти или если он выполняет фактическое изменение размера, как в realloc (3), он полностью усматривает компилятор. Вы можете изучить текущее состояние и, возможно, поднять issue, если есть место для улучшения этого.

Однако это, вероятно, микро-оптимизация. Первый источник повышения производительности почти всегда заключается в выборе лучшего алгоритма и/или лучшей структуры данных. Использование вектора с большим размером, чтобы, наконец, сохранить несколько элементов, вероятно, не самый лучший вариант для потребления памяти.

РЕДАКТОР: Вышеуказанное только частично правильно. В общем случае компилятор не может получить, если есть другие указатели на массив поддержки среза. Таким образом, realloc неприменим. Вышеприведенный фрагмент фактически гарантированно отображает копию элементов 'newSize'. Извините за возможные путаницы.

Ответ 2

Go не имеет способ уменьшить кусочки. Это не проблема в большинстве случаев, но если вы профилируете использование своей памяти и обнаружите, что используете слишком много, вы можете что-то сделать:

Во-первых, вы можете просто создать срез требуемого размера и скопировать свои данные в него. Затем сборщик мусора освободит большой ломтик. Копировать встроенный

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

В заключение вы можете использовать 1e7 вместо math.Pow10(7).

Ответ 3

Кроме того, вы можете повторно использовать большую часть выделенной памяти во время работы своего приложения, посмотрите: пакет bufs

PS, если вы повторно распределите новую память для меньшего фрагмента, старая память может не быть освобождена в одно и то же время, она будет освобождена, когда сборщик мусора решает.

Ответ 4

Посмотрите на этот пример:

func main() {
    s := []string{"A", "B", "C", "D", "E", "F", "G", "H"}
    fmt.Println(s, len(s), cap(s))  // slice, length, capacity

    t := s[2:4]
    fmt.Println(t, len(t), cap(t))

    u := make([]string, len(t))
    copy(u, t)
    fmt.Println(u, len(u), cap(u))
}

Он производит следующий вывод:

[A B C D E F G H] 8 8
[C D] 2 6
[C D] 2 2

s - это срез, содержащий 8 строк. t - это фрагмент, который сохраняет часть [C D]. Длина t равна 2, но поскольку он использует тот же скрытый массив s, его емкость равна 6 (от "C" до "H" ). Возникает вопрос: как иметь срез [C D], который не зависит от скрытого массива s? Просто создайте новый фрагмент строк длиной 2 (срез u) и скопируйте содержимое t в u. u скрытый массив отличается от скрытого массива s.

Первоначальная проблема заключалась в следующем: у вас большой кусочек, и вы создаете на нем небольшой кусочек меньшего размера. Поскольку меньший фрагмент использует тот же скрытый массив, сборщик мусора не будет удалять скрытый массив.

См. нижнюю часть этого сообщения для получения дополнительной информации: http://blog.golang.org/go-slices-usage-and-internals.