Меня часто спрашивают, почему указатели лучше? Шёрт побери, вот почему!
Описание функций меняющих местами значения переменных:
1. Функция с указателями:
func swapValues(first, second *int) {
fmt.Println("Перед:", *first, *second)
temp := *first
*first = *second
*second = temp
fmt.Println("После:", *first, *second)
}
Эта функция использует указатели на целые числа (*int
). При вызове функции передаются адреса переменных, что позволяет функции изменять реальные значения переменных в вызывающей среде. Это называется передачей по ссылке.
2. Функция с передачей значений:
func swapValues(first, second int) {
fmt.Println("Перед:", first, second)
temp := first
first = second
second = temp
fmt.Println("После:", first, second)
}
Эта функция принимает копии значений first
и second
(типа int
). Она работает с этими копиями локально, что означает, что оригинальные значения в вызывающей среде остаются неизменными. Это называется передачей по значению.
Сравнение аспектов:
Аспект |
Первая функция (с указателями) |
Вторая функция (с передачей значений) |
---|
Передача параметров |
+ |
- |
Использование памяти |
+ |
- |
Изменение данных вызвавшей стороны |
+ |
- |
Накладные расходы на разыменование |
- |
+ |
Накладные расходы на копирование |
+ |
- |
Производительность |
+ |
- |
Случай использования |
+ |
- |
Эффективность работы с кэшом |
+ |
- |
Подходит для больших данных |
+ |
- |
Влияние на оригинальные значения |
+ |
- |
Итого |
8+ |
2+ |
Подробности по каждому аспекту:
-
Передача параметров:
Первая функция использует указатели, что позволяет изменять оригинальные значения в вызывающей функции (передача по ссылке).
Вторая функция работает с копиями значений и не изменяет исходные данные (передача по значению).
-
Использование памяти:
Первая функция передаёт указатели, которые занимают меньше памяти (4 или 8 байт), поэтому она использует меньше памяти.
Вторая функция создаёт копии значений, что приводит к большему расходу памяти, особенно для больших данных.
-
Изменение данных вызвавшей стороны:
Первая функция изменяет оригинальные значения в вызывающей среде.
Вторая функция изменяет только локальные копии значений.
-
Накладные расходы на разыменование:
Первая функция использует разыменование указателей, что добавляет небольшие накладные расходы.
Вторая функция работает напрямую с копиями значений, что делает доступ к данным немного быстрее.
-
Накладные расходы на копирование:
Первая функция не копирует данные, передавая только указатели.
Вторая функция создаёт копии значений, что может быть накладно для больших данных.
-
Производительность:
Первая функция быстрее для больших данных, так как она избегает копирования.
Вторая функция может быть быстрее только для небольших типов данных (таких как
int
), но для больших данных она менее производительна из-за необходимости копирования.
-
Случай использования:
Первая функция лучше подходит, когда нужно изменить исходные данные.
Вторая функция полезна, если требуется работать с копиями данных без изменения оригинала.
-
Эффективность работы с кэшом:
Первая функция более эффективна при работе с большими данными, так как изменяет данные на месте, без создания копий.
Вторая функция менее эффективна, так как работает с копиями данных.
-
Подходит для больших данных:
Первая функция эффективнее для работы с большими структурами данных или массивами, так как не требует копирования.
Вторая функция менее эффективна, так как создаёт копии данных.
-
Влияние на оригинальные значения:
Первая функция изменяет оригинальные значения, что полезно в случаях, когда это необходимо.
Вторая функция не влияет на оригинальные значения, так как работает с копиями.
Итоговое сравнение:
Аспект |
Первая функция (с указателями) |
Вторая функция (с передачей значений) |
---|---|---|
Итого |
8+ |
2+ |
Первая функция выигрывает в 8 аспектах, а вторая — в 2 аспектах.
Заключение:
Первая функция (с указателями) в 4 раза лучше по общим параметрам, чем вторая функция (с передачей значений).
Ну вот и все! Ж:-)
Комментарии (7)
evgeniy_kudinov
23.10.2024 07:55Где пруфы, Билли? Нам нужны пруфы
Извините, но без бенчмарков и реальных цифр эти умозрения типа "Сферический конь в вакууме" и более подходят менеджерам. Напишите бенчмарки для каждого пункта утверждения, пожалуйста.Sly_tom_cat
23.10.2024 07:55package main import ( "testing" ) var ( a1 = 10 a2 = 20 ) func swapValuesP(first, second *int) (*int, *int) { temp := *first *first = *second *second = temp return first, second } func swapValuesV(first, second int) (int, int) { temp := first first = second second = temp return first, second } func BenchmarkSwapValuesP(b *testing.B) { for range b.N { swapValuesP(&a1, &a2) } } func BenchmarkSwapValuesV(b *testing.B) { for range b.N { swapValuesV(a1, a2) } }
goos: linux goarch: amd64 pkg: pointer_vs_copy cpu: AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx BenchmarkSwapValuesP BenchmarkSwapValuesP-8 400667139 2.948 ns/op 0 B/op 0 allocs/op BenchmarkSwapValuesV BenchmarkSwapValuesV-8 1000000000 0.4211 ns/op 0 B/op 0 allocs/op PASS ok pointer_vs_copy 1.990s
NO COMMENTS
kravlad
23.10.2024 07:55В статье и написано, что инты эффективнее по значению передавать. Что вы хотели этим бенчмарком показать?
RodionGork
23.10.2024 07:55вот это тема, вот это открытие :)
попробуйте мьютекс или waitgroup передать в функцию по указателю и без. можно получить удивительнейшие результаты.
ShaggyCat2
23.10.2024 07:55Хотелось бы увидеть юзкейсы, где лучше применять функции с указателем или без и какие будут плюсы-минусы. Без юзкейсов всё это слишком неопределенно.
serjeant
23.10.2024 07:55Очень странно, почему вы не использовали синтаксис go для обмена значениями переменных:
first,second = second,first
*first,*second = *second,*first
, кстати прежде чем разыменовывать указатели, было бы неплохо их на nil проверить.
Posya
Чем яблоко лучше автомобиля:
Оно легче помещается в карман
Оно слаще
....
PROFIT!