6 советов по повышению производительности в Go

Цель статьи — обсудить 6 советов, которые могут помочь в диагностике и устранении проблем с производительностью в ваших приложениях Go.

@golang_interview – golang разбор вопросов с собеседований.

Бенчмаркинг

Написание эффективных тестов на Go имеет решающее значение для высокой производительности вашего кода. Создадим файл для теста «becnh_test» и испольем функцию Benchmark пакета тестирования. Вот пример:

6 советов по повышению производительности в Go

В этом примере мы считаем время, необходимое для вычисления 20-го числа Фибоначчи. Функция BenchmarkFibonacci запускает функцию fibonacci b.N раз. Это является значением, установленным пакетом тестирования для получения статистически значимого результата.

Чтобы интерпретировать результаты тестов, мы можем запустить go test -bench=. -benchmem в терминале. Этот код выполнит все тесты в текущем каталоге и выведет статистику распределения памяти. Флаг -bench используется для указания регулярного выражения для сопоставления имён тестов, а . будет соответствовать всем тестам в текущем каталоге. Флаг -benchmem будет выводить статистику выделения памяти вместе с результатами синхронизации.

Профилирование

В Go есть встроенные инструменты профилирования, которые помогут вам понять, что делает ваш код. Наиболее распространенным инструментом профилирования является CPU profiler, который можно включить, добавив флаг -cpuprofile в команду go test. Вот пример:

6 советов по повышению производительности в Go

Первая функция, «TestFibonacci», представляет собой простой модульный тест, который проверяет, правильно ли функция фибоначчи возвращает 20-е число в последовательности фибоначчи.

Функция «fibonacci» — это рекурсивная реализация последовательности фибоначчи, которая вычисляет n-е число в последовательности.

Функция «BenchmarkFibonacci» — это тест, который запускает функцию «Fibonacci» 20 раз и измеряет время её выполнения.

Функция «ExampleFibonacci» — это пример, который выводит 20-е число в последовательности фибоначчи с помощью функции «fibonacci» и проверяет, равно ли оно ожидаемому значению 6765.

Чтобы включить профилирование, мы используем флаг «-cpuprofile» с командой «go test», чтобы вывести результаты профилирования в файл с именем «prof.out». Для запуска тестов и создания данных профилирования можно использовать следующую команду:

6 советов по повышению производительности в Go

После запуска тестов мы можем использовать команду «go tool pprof» для анализа данных профилирования. Мы можем запустить инструмент pprof с помощью следующей команды:

6 советов по повышению производительности в Go

Это откроет интерактивную оболочку pprof, где мы можем вводить различные команды для анализа данных профилирования. Например, мы можем использовать команду «top», чтобы отобразить функции, потребляющие больше всего процессорного времени:

6 советов по повышению производительности в Go

Это отобразит список функций с наибольшим использованием процессорного времени, отсортированных по процессорному времени. В этом случае мы должны увидеть функцию «fibonacci» вверху списка, так как она потребляла больше всего процессорного времени во время теста.

Мы также можем использовать команду «web» для отображения данных профилирования в графическом формате и команду «list» для отображения исходного кода, аннотированного данными профилирования.

Профилирование — это мощный инструмент, который может помочь нам определить узкие места производительности в нашем коде. Используя флаг «-cpuprofile» и инструмент pprof, мы можем легко генерировать и анализировать данные профилирования для наших тестов и приложений Go.

Оптимизация

Компилятор Go выполняет несколько оптимизаций, включая встраивание, escape-анализ и удаление мёртвого кода. Встраивание — это процесс замены вызова функции телом функции, что может повысить производительность за счёт уменьшения расходов памяти на вызов функции. Escape-анализ — это процесс определения того, занят ли адрес переменной, что может помочь компилятору разместить его в стеке, а не в куче. Удаление мёртвого кода — это процесс удаления кода, который никогда не выполняется.

Встраивание

6 советов по повышению производительности в Go

В первом примере функция add вызывается с аргументами 3и 4, что приводит к накладным расходам на вызов функции. Во втором примере вызов функции заменяется фактическим кодом, что приводит к более быстрому выполнению.

Escape-анализ

6 советов по повышению производительности в Go

В этом примере переменная a размещается в стеке, так как её адрес не вызывается. Однако переменная b выделяется в куче, так как её адрес вызывается оператором &.

Подробнее о Escape-анализе

6 советов по повышению производительности в Go

В функции createUser создаётся новый User и возвращается его адрес. Обратите внимание, что значение User размещается в стеке с момента возврата его адреса, поэтому оно не уходит в кучу.

Если мы добавим строку, которая принимает адрес значения User перед его возвратом:

6 советов по повышению производительности в Go

Теперь адрес значения User берётся и сохраняется в возвращаемой переменной. Это приводит к тому, что значение уходит в кучу, а не распределяется в стеке.

Escape-анализ важен, потому что выделение кучи дороже, чем выделение стека, поэтому минимизация выделения кучи может повысить производительность.

Удаление мёртвого кода

6 советов по повышению производительности в Go

В этом примере код внутри оператора if никогда не выполняется, поэтому он удаляется компилятором при устранении мёртвого кода.

Использование трассировщика GO

Трассировщик в Go может дать подробную информацию о том, что происходит в программе, включая трассировку стека, блокировку горутин и многое другое. Вот пример того, как его использовать:

6 советов по повышению производительности в Go

В этом примере мы создаём файл трассировки, запускаем трассировку, а затем останавливаем её. При запуске программы данные трассировки будут записаны в файл trace.out. Затем вы можете проанализировать эти данные трассировки, чтобы лучше понять, что происходит в вашей программе.

Управление памятью и настройка GC

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

6 советов по повышению производительности в Go

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

Конкурентность

Однако важно правильно использовать функции конкурентности, чтобы избежать таких проблем, как состояние гонки и взаимоблокировки. Вот пример того, как использовать каналы для безопасного общения между горутинами:

6 советов по повышению производительности в Go

Оператор make(chan int) создаёт канал, который используется для передачи целочисленного значения между двумя горутинами.

Первая горутина создаётся с оператором go func() {...}(), который отправляет значение 1 в канал ch после ожидания в течение 1 секунды. Это означает, что через 1 секунду канал ch будет иметь значение 1.

Вторая горутина создаётся с оператором select, который ожидает связи на канале ch. Если из канала получено значение, выводится сообщение «Received message». Если значение не получено в течение 2 секунд, выводится сообщение «Timed out».

Таким образом, хотя между оператором и первой горутиной нет явной связи select, всё же связь происходит через общий канал ch.

Заключение

Надеюсь, что данная статья была полезна для вас!

+1
4
+1
3
+1
0
+1
0
+1
1

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *