Skip to content

Вебинар "Некоторые техники оптимизации Go-кода"

Notifications You must be signed in to change notification settings

s-shpak/webinar-optimizations

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Вебинар "Некоторые техники оптимизации Go-кода"

strconv vs fmt

Запустим бенчмарки в cmd/strconv_vs_fmt:

go test -bench=.

Что наблюдаем и почему?

Соберем информацию по выделению памяти:

go test -bench=. -benchmem

Теперь соберем профиль CPU:

go test -bench=. -cpuprofile cpu.out

И запустим pprof для его визуализации (документация: https://github.com/google/pprof/blob/main/doc/README.md#interpreting-the-callgraph):

go tool pprof cpu.out

Как объяснить полученный результат в контексте бенчмарок?

Соберем профиль памяти:

go test -bench=. -memprofile mem.out

И запустим pprof для его визуализации:

go tool pprof mem.out

Что наблюдаем?

Объяснение устройства интерфейсов: см. слайды.

Убедимся в том, что FormatIntSprintf действительно аллоцирует память при передаче i в fmt.Sprintf (это также можно сделать при помощи команды list pprof: list optimizations/cmd/strconv_vs_fmt.FormatIntSprintf):

go build -gcflags '-S -N' main.go &> main.s

Определение convT64 находится в исходном коде Go в src/runtime/iface.go.

Вопрос: разобрав как устроены интерфейсы в Go, посмотрите на 9 строку файла cmd/iface_puzzle/decl.go. Как вы думаете, что она делает?

Несколько слов об Escape Analysis

Запустим бенчмарки с дополнительным флагом:

go test -gcflags '-m' -bench=.

Разница между стеком и кучей: см. слайды.

Использование указателей как ключей map

См. пример в cmd/map_string_keys.

Как изменятся измерения, если поменять тип ключа в мапе на int?

См. определение структуры string в исходниках Go в src/runtime/string.go.

Определение емкости слайса

Перед запуском бенчмарок посмотрите на код в cmd/slices_cap/main.go и ответьте:

  • какая из версий будет работать быстрее?
  • будет ли у них одинаковое потребление памяти?

Запустим бенчмарки в cmd/slices_cap:

go test -bench=. -benchmem

Разберем устройство слайсов: см. слайды и src/runtime/slice.go.

Понаблюдаем как меняется емкость слайсов:

go test ./... -run=TestPrintSliceCapacityChanges -v

Bounds-checking elimination

Попробуем запустить тест TestSumFirstElementsOfSlice из cmd/bce:

go test  ./... -bench=this-bench-does-not-exist -run=TestSumFirstElementsOfSlice

Теперь отключим bounds checking:

go test -bench=this-bench-does-not-exist -gcflags='-B' ./... -run=TestSumFirstElementsOfSlice

Что наблюдаем?

Посмотрим на разницу в ассемблерном коде:

go build -gcflags '-S -N' main.go &> main.s

go build -gcflags '-S -N -B' main.go &> main-no-bc.s

Проверим, что версия без проверок действительно быстрее:

rm ./*.s

go test -run=this-test-does-not-exist -bench="^BenchmarkSumFirstElementsOfSlice$"

go test -run=this-test-does-not-exist -gcflags="-B" -bench="^BenchmarkSumFirstElementsOfSlice$"

Go позволяет узнать, на какой строке происходит bound check:

go test -gcflags '-d=ssa/check_bce/debug=1' -run=this-test-does-not-exist  -bench="^BenchmarkSumFirstElementsOfSlice$"

Как мы видим bound check происходит внутри цикла. Можно ли это оптимизировать?

Запустим бенчмарки для новой функции:

go test -gcflags="-d=ssa/check_bce/debug=1" -bench="^BenchmarkSumFirstElementsOfSliceBCE$" -run=this-test-does-not-exist  -count=5

Посмотрите на код новой функции в main_bce.go.

sync.Pool

Документация к sync.Pool находится здесь: https://pkg.go.dev/sync#Pool

Код примера находится в sync-pool.

Запустим пример без использования sync.Pool:

go test -run TestNoPool -memprofile mem.out -v -count=2

и с sync.Pool:

go test -run TestWithPool -memprofile mem-pool.out -v -count=2

PGO

https://habr.com/en/companies/yandex_praktikum/articles/729570/

About

Вебинар "Некоторые техники оптимизации Go-кода"

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published