Подтвердить что ты не робот

Ломтики структур против ломтиков указателей на структуры

Я часто работаю с фрагментами структур. Вот пример такой структуры:

type MyStruct struct {
    val1, val2, val3    int
    text1, text2, text3 string
    list                []SomeType
}

Итак, я определяю свои фрагменты следующим образом:

[]MyStruct

Скажем, у меня около миллиона элементов, и я сильно работаю с срезом:

  • Я часто добавляю новые элементы. (Общее количество элементов неизвестно.)
  • Я сортирую его время от времени.
  • Я также удаляю элементы (хотя и не столько как добавление новых элементов).
  • Я часто читаю элементы и передаю их (как аргументы функции).
  • Содержимое самих элементов не изменяется.

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

[]*MyStruct

Теперь структуры остаются там, где они есть, и мы имеем дело только с указателями, которые, как я предполагаю, имеют меньший размер и, следовательно, делают мои операции быстрее. Но теперь я даю сборщику мусора намного больше работы.

  • Можете ли вы предоставить общие рекомендации по работе с структурами напрямую или при работе с указателями на структуры?
  • Должен ли я беспокоиться о том, сколько работы я оставляю в GC?
  • Является ли служебная нагрузка при копировании структуры или копировании указателя пренебрежимо малой?
  • Возможно, миллион элементов не так много. Как все это изменяется, когда срез становится намного больше (но, конечно, все равно в ОЗУ)?
4b9b3361

Ответ 1

Просто об этом подумал. Ранены некоторые ориентиры:

type MyStruct struct {
    F1, F2, F3, F4, F5, F6, F7 string
    I1, I2, I3, I4, I5, I6, I7 int64
}

func BenchmarkAppendingStructs(b *testing.B) {
    var s []MyStruct

    for i := 0; i < b.N; i++ {
        s = append(s, MyStruct{})
    }
}

func BenchmarkAppendingPointers(b *testing.B) {
    var s []*MyStruct

    for i := 0; i < b.N; i++ {
        s = append(s, &MyStruct{})
    }
}

Результаты:

BenchmarkAppendingStructs  1000000        3528 ns/op
BenchmarkAppendingPointers 5000000         246 ns/op

Возьмись: мы в наносекундах. Вероятно, пренебрежимо мало для небольших ломтиков. Но для миллионов ops это разница между миллисекундами и микросекундами.

Btw, я снова попробовал запустить тест с срезами, которые были предварительно выделены (с емкостью 1000000), чтобы устранить накладные расходы из append(), периодически копируя базовый массив. Добавляемые структуры упали на 1000 нс, добавляющие указатели не изменились вообще.

Ответ 2

Можете ли вы предоставить общие рекомендации по работе с структурами напрямую или при работе с указателями на структуры?

Нет, это слишком сильно зависит от всех других факторов, о которых вы уже упоминали.

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

(Тем не менее, моя интуиция будет заключаться в использовании указателей и, возможно, sync.Pool, чтобы помочь сборщику мусора: http://golang.org/pkg/sync/#Pool)