Комментарии 18
Если мы итерируемся по слайсу так:
for i, v := range slice
то в переменную
v
попадает копия слайса
Копия элемента слайса?
Есть разница в производительности между передачей параметра по ссылке и по значению (нет)
Будем использовать такую эталонную функцию:
// наш эталон производительности
var testInt64 int64
Для полноценной демонстрации желательно было использовать структуру с большим количеством полей. Насколько мне известно именно об этой оптимизации говорят, когда объясняют разницу между передачей по ссылке и по значению
make([]T, 0, n)
По-моему если у вас этот код не критичен про производительности то лучше так не делать. Это просто увеличивет нагрузку на программиста читающего код без явной выгоды.
Зато легко пропустить ошибку в стиле
newarr := make([]int, len(arr), 0) // кто-то перепутал параметры местами
newarr = append(newarr, 1) // теперь у нас в начале неожиданные нули
Uber Go Style Guide с вами не согласится. Если можно подсказать gc сколько нужно аллоцировать памяти, то лучше это сделать если важна эффективность. К таким конструкциям не сложно привыкнуть, но да, даже где-то в go блоге писали, что это частая ошибка - когда путают len с capacity
Если важна эффективность, если ваши коллеги не пропускают глупых ошибок, если у вас высокая производительность и высокий уровень кода в компании то да, почему бы и нет.
Но если у вас простой тупой код, который на запросы к базе и по сети тратит на порядки больше чем на сам го то можно вполне сделать код чуть проще ради того чтобы избежать глупой ошибки и чуть-чуть повысить читаемость.
нет, не легко. Такую ошибку тяжело списать на невнимательность, и она легко всплывает при взгляде на код. Но даже в таком случае, это не оправдание - есть очень (слишком) много мест, где допустить ошибку по невнимательности гораздо проще. Это не та вещь, где действительно следует жертвовать мнимой читаемостью
почему читаемость мнимая?
по вашему код
y := make([]int, 0, len(something))
читается также легко как и:
x := []int{}
Потому что для меня например это приведет к тому что я пойду проверять что там за массив something
то есть эта строка кода меня пусть немного но отвлечет
не знаю, насколько это отвлечет вас, но у меня это займет дай боже лишних пол секунды, за которые я успею нажатием колесика мыши метнуться к определению (а в лучшем случае - something будет парой строчек выше). Гораздо больше усилий у меня займет понятие архитектуры и костылей, поэтому, как любит выражаться гофер - экономия на спичках.
Разница в передаче по указателю/значению начинается на структурах от 16 байт и выше (ниже - выгоднее передавать по значению по возможности) - тест в статье ничего не показывает.
Смотря как передавать
Если с утечкой в хип (пример, при вызове метода интерфейса, замыкания и тд), то пусть структура хоть 300 байт весит — всё равно лучше передать по значению. Есть еще дополнительные нюансы, но в целом правило такое: лучше всегда передавать по значению там, где это возможно. Так GC и latency скажут спасибо
Если структура гарантировано не утечёт в хип, то да, лучше по указателю если структура занимает от двух машинных слов
Какая-то пустоватая статья, содержит кучу давно известных вещей (про defer, context, make, не самую быструю библиотеку json) и некоторые голословные заявления...
Не указана версия go, на которой выполнялись бенчмарки.
Нет ссылки на репозиторий, чтобы перепроверить результаты.
В параграфе "Есть разница в производительности между передачей параметра по ссылке и по значению":
есть уверенность, что вызов функций не превратился в inline?
а где пример для большой структуры или массива?
В параграфе "Оптимизация итерации по массиву" нет примера итерации:
через индекс
через slice
Кроме того, это не особо полезная информация, так как в коде редко можно встретить массив из структур. Скорее всего это будет сразу срез (slice).
Всем давно известно, что
крайне сомнительное утверждение
Лучше избегать использования
defer
в цикле.defer
приводит к увеличению стека, а стек очищается только после завершения функции; к тому же, это может привести к не очень очевидным ошибкам.
Вот тут начинается какая-то дичь. Стек в го по умолчанию фиксирован, обычно размером в 4МиБ, и расти из-за цикла самостоятельно не может, насколько мне известно. defer рассказывает gc в какой момент надо удалять элементы и тот видимо под капотом пихает это в динамический массив, то бишь в кучу, а не на стек. Ну и как обычно - рассказали как не надо, но не показали как надо.
Есть разница в производительности между передачей параметра по ссылке и по значению (нет)
а если передавать тяжелую структуру?
Пара мифов не развеяна, потому что в обоих случаях функции инлайнятся, поэтому анонимной функции просто не будет, а передача по указателю превратится в инкремент значения по указателю, а поскольку в этом указателе больше не будет смысла, то компилятор спокойно и это оптимизирует. Разница в производительности между передачей по ссылке и по значению кроется в тех случаях, когда использование указателя приводит к выделению памяти в куче, либо когда на стек копируется очень большой объект. С анонимными функциями тоже тема не раскрыта, так как используется очень простой случай, когда анонимная функция инлайнится. Разница должна быть в самом факте вызова функции
Я не эксперт в том, что там компилятор Go может наколдовать, но мне видится так, что сами по себе вызовы BenchmarkDirect(), а потом еще и incDirect() помещают в стек свои адреса возврата и переключают адресацию локальных переменных на новые области стека. Так каким же образом тогда получается доступ к переменной testInt64, оставшейся в другой области видимости? Очевидно, только по ссылке/указателю. Что в общем-то и подтверждают результаты теста.
// наш эталон производительности
var testInt64 int64
func BenchmarkDirect(b *testing.B) {
for i := 0; i < b.N; i++ {
incDirect()
}
}
func incDirect() {
testInt64++
}
Ускоряем Go: известные и не очень методы оптимизации и связанные с ними штуки