Когда вы задумаетесь об оптимизации?
- Когда функция вызывается часто
- Когда дизайн интерфейса или структуры данных нецелесообразен, а использование памяти слишком велико.
- Ответ интерфейса занимает слишком много времени
- Когда код слишком беспорядочный, часто возникают проблемы
Какие существуют способы поиска проблем?
- Испытание под давлением одного интерфейса (обычно это новый интерфейс или при смене интерфейса), испытание под давлением с фиксированным QPS
- Испытание на экстремальное давление QPS (обычно используется для проверки максимального диапазона давления интерфейса)
- Полноканальный стресс-тест (этот раздел в основном предназначен для подготовки к пикам трафика)
Некоторые инструменты проверки кода
Инструменты оценки кода Go:
- goreporter — создание отчетов об оценке качества кода Go.
- dingo-hunter — Статический анализатор для поиска взаимоблокировок в программах на Go
- flen — Получить информацию о длине функции в пакетах Go
- go/ast — пакет ast объявляет типы, используемые пакетами Go для представления синтаксических деревьев.
- gocycle — Измерение сложности цикломатической функции в исходном коде Go
- Go Meta Linter — одновременный инструмент Go lint и стандартизация вывода инструмента.
- go vet — проверить исходный код Go и сообщить о подозрительных конструкциях.
- ineffassign — обнаружение недопустимых присвоений в коде Go
- Safesql — инструмент статического анализа Golang для предотвращения SQL-инъекций.
некоторые инструменты
- benckmark
- При использовании различных инструментов, библиотек с открытым исходным кодом и т. д. вы можете сначала протестировать метод. Затем решите, какой из них использовать в конце
- go pprof
- CPU Профиль: используется для диагностики логики кода, вызывающей высокую загрузку ЦП.
- Профиль памяти: используется для диагностики логики кода, вызывающей высокое использование памяти.
- Профиль Goroutine: используется для диагностики логики кода, которая заставляет Goroutine занимать высокий уровень, и анализа конкретной логики выполнения Goroutine.
- Block Профиль: используется для диагностики логики кода, которая блокирует выполнение, например обнаружения больших блокировок (мьютекс блокирует долго выполняющуюся логику, и есть много других горутинов, ожидающих этой блокировки).
Встроенный профилировщик памяти Go позволяет нам анализировать использование памяти онлайн-системами. Есть четыре соответствующих индикатора:
- inuse_objects: обращаем внимание, когда нам кажется, что в памяти слишком много резидентных объектов. на Индикатор
- inuse_space: Когда мы думаем, что приложение занимает RSS Когда он слишком большой, он будет сосредоточен на Индикатор
- alloc_objects: Когда приложение в истории испытывало большое количество выделенной памяти, это приведет к CPU Или когда использование памяти значительно возрастает, оно может сосредоточиться на Индикатор
- alloc_space: Когда приложение в истории испытало значительное увеличение использования памяти, оно сосредоточится на Индикатор
Далее мы остановимся на этих аспектах.
Slice
- Выделите память для срезов заранее
○ При необходимости используйте третий параметр: make([]T, 0, len)
○ Если точное число заранее неизвестно и срез является временным, его можно установить больше, если срез не будет увеличиваться во время выполнения.
- Не забудьте использовать «копировать»
○ Старайтесь не использовать добавление при копировании, например, при объединении двух или более фрагментов.
- Не оставляйте неиспользованные кусочки
○ Если вам нужно отрезать от ломтика небольшой кусок и использовать только его, основная часть сохранится. Вы можете использовать копирование для создания нового фрагмента, и старый объект будет переработан сборщиком мусора.
string
- strings.Builder,bytes.Bufferпохожий
- Предпочитайте strings.Builder +=
struct
- Уменьшить путем выравнивания памятиstructразмер
- Структуру можно выровнять (в правильном порядке в зависимости от размера полей), тем самым уменьшив размер самой структуры.
- Траверс []struct{} Используйте индексы вместо range
- Иногда, когда мы используем канал, он обычно используется для представления идентификатора, а не для отправки данных для уведомления подпрограмм о выполнении некоторых задач. Эта ситуация более практична. Пустая структура
defer
- Постарайтесь не использовать defer или, по крайней мере, не используйте defer в цикле.
map
- То же, что и срез,Необходимо заранее выделить память
- При инициализации карты укажите ее размер
- Если необходимо представить заполнитель, его значение представляется пустой структурой.
- Прозрачныйmap
- Карта может только увеличиваться, а не уменьшаться. Нужно это контролировать - полностью и явно сбросить карту
- Использование указателей
- если map Не содержит указателей. Имейте в виду, что строки тоже являются указателями — используйте в качестве ключей []byte вместо строк.
interface
- Рассчитать распределение памяти
○ Помните: чтобы присвоить значение интерфейсу, сначала нужно его куда-то скопировать, а затем вставить указатель. Ключевое слово — копия. Получается, что стоимость упаковки и распаковки будет примерно равна размеру структуры и распределению.
sync.Pool
быстрыйhttp. Он поддерживает почти все объекты с помощью sync.Pool, поэтому утверждает, что работает в 10 раз быстрее, чем http, но на самом деле это не так.
пул сопрограмм
В большинстве сценариев приложений go не требует пула. сопрограммы. Конечно, пул сопрограммы все же имеют некоторые преимущества:
- Количество горутин можно ограничить, чтобы избежать неограниченного роста.
- Уменьшите количество раз расширения стека.
- В сценариях, где горутины создаются часто, ресурсы используются повторно для экономии памяти. (Для этого требуется определенный масштаб. В общих сценариях эффект не очевиден.)
Go имеет определенные возможности повторного использования горутин. Таким образом, вам придется выбирать, использовать ли пул в соответствии со сценарием. сопрограмм,Неуместные сцены не только не принесут никакой пользы,Наоборот, это увеличивает сложность системы.
Уменьшите потребление блокировки
В параллельных сценариях критические разделы обычно блокируются. К рискам производительности, связанным с этим, также следует относиться серьезно. Общие методы оптимизации включают в себя:
- Уменьшите степень детализации блокировок: в стандартной библиотеке go math.rand таит в себе такую скрытую опасность. Когда мы напрямую используем библиотеку rand для генерации случайных чисел, за их генерацию фактически отвечает глобальный объект globalRand. Генерация случайных чисел после блокировки globalRand приведет к неэффективности в сценариях, где мы часто используем случайные числа.
- Атомарный: в соответствующих сценариях использование атомарных операций для замены блокировок мьютексов также является классическим методом без блокировок. Оптимизация sync.map в стандартной библиотеке для операций чтения исключает rwlock, что является стандартным случаем. О нем написано много статей, поэтому я не буду вдаваться в подробности.
Объединение нескольких маленьких объектов в один большой объект
Уменьшите количество ненужных косвенных ссылок на указатели и больше используйте ссылки на копирование.
Например, используйте bytes.Buffer вместо *bytes.Buffer, поскольку при использовании указателя для завершения ссылки будут выделены 2 объекта.
оптимизация, потребляющая много времени процессора
- При изготовлении заранее оцените размер.
- Временные карты и фрагменты используют sync.pool
- Если он больше 32 КБ, вы также можете использовать sync.pool.
- Не злоупотребляйте горутиной и уменьшите давление gc
- Не злоупотребляйте мьютексом и сократите переключение контекста.
- []Используйте unsafe для преобразования между байтовыми и строковыми временными переменными.
- Сократите использование отражения и отсрочки
- использование атомной блокировки без блокировки