«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > Golang: как наблюдаемость и профилирование выявили почти необнаружимое троттлинг

Golang: как наблюдаемость и профилирование выявили почти необнаружимое троттлинг

Опубликовано 8 ноября 2024 г.
Просматривать:402

В личном проекте Go, который получает информацию о финансовых активах от Bovespa.
Система интенсивно использует параллелизм и параллелизм с горутинами, обновляя информацию об активах (наряду с бизнес-расчетами) каждые 8 ​​секунд.
Первоначально никаких ошибок или предупреждений не появлялось, но я заметил, что некоторые горутины выполнялись дольше, чем другие.

Если быть более конкретным, то время p99 составляло 0,03 мс, в некоторые моменты оно увеличивалось до 0,9 мс. Это побудило меня глубже изучить проблему.

Я обнаружил, что использую пул горутин семафоров, который был создан на основе переменной GOMAXPROCS.
Однако я понял, что в этом подходе есть проблема.

Когда мы используем переменную GOMAXPROCS, она неправильно фиксирует количество ядер, доступных в контейнере. Если в контейнере доступно меньше ядер, чем общее количество виртуальной машины, учитывается общее количество виртуальных машин. Например, у моей виртуальной машины есть 8 доступных ядер, но в контейнере их было только 4. В результате было создано 8 горутин для одновременной работы, что вызвало регулирование.

После долгих исследований за ночь я нашел библиотеку, разработанную Uber, которая более эффективно автоматически настраивает переменную GOMAXPROCS, независимо от того, находится она в контейнере или нет. Это решение оказалось чрезвычайно стабильным и эффективным: automaxprocs

Golang: Como a observabilidade e profiling revelaram um throttling quase indetectável убер-гоу / автомакспрокс

Автоматически настройте GOMAXPROCS в соответствии с квотой процессора контейнера Linux.

automaxprocs Golang: Como a observabilidade e profiling revelaram um throttling quase indetectável Golang: Como a observabilidade e profiling revelaram um throttling quase indetectável Golang: Como a observabilidade e profiling revelaram um throttling quase indetectável

Автоматически устанавливать GOMAXPROCS в соответствии с квотой процессора контейнера Linux.

Установка

go get -u go.uber.org/automaxprocs

Быстрый старт

import _ "go.uber.org/automaxprocs"

func main() {
  // Your application logic here.
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Производительность

Данные получены с помощью внутреннего балансировщика нагрузки Uber. Мы запустили балансировщик нагрузки с квотой ЦП 200 % (т. е. с 2 ядрами):

GOMAXPROCS RPS P50 (мс) P99,9 (мс)
1 28 893,18 1,46 19,70
2 (равен квоте) 44 715,07 0,84 26,38
3 44 212,93 0,66 30.07
4 41 071,15 0,57 42,94
8 33 111,69 0,43 64,32
По умолчанию (24) 22 191,40 0,45 76,19

Когда GOMAXPROCS увеличивается сверх квоты ЦП, мы видим незначительное уменьшение P50, но значительное увеличение до P99. Мы также видим, что общее количество обработанных запросов в секунду также уменьшается.

Когда GOMAXPROCS превышает выделенную квоту ЦП, мы также наблюдаем значительное регулирование:

$ cat /sys/fs/cgroup/cpu,cpuacct/system.slice/[...]/cpu.stat
nr_periods 42227334
nr_throttled 131923
throttled_time 88613212216618

После того как GOMAXPROCS был уменьшен в соответствии с квотой ЦП, мы не заметили регулирования ЦП.

Просмотреть на GitHub
.

После реализации использования этой библиотеки проблема решилась, и теперь время p99 оставалось постоянно на уровне 0,02 мс. Этот опыт подчеркнул важность наблюдаемости и профилирования в параллельных системах.

Ниже приведен очень простой пример, но демонстрирующий разницу в производительности.

Используя встроенное тестирование Go и пакет benckmak, я создал два файла:

benchmarking_with_enhancement_test.go:

package main

import (
    _ "go.uber.org/automaxprocs"
    "runtime"
    "sync"
    "testing"
)

// BenchmarkWithEnhancement Função com melhoria, para adicionar o indice do loop em um array de inteiro
func BenchmarkWithEnhancement(b *testing.B) {
    // Obtém o número de CPUs disponíveis
    numCPUs := runtime.NumCPU()
    // Define o máximo de CPUs para serem usadas pelo programa
    maxGoroutines := runtime.GOMAXPROCS(numCPUs)
    // Criação do semáforo
    semaphore := make(chan struct{}, maxGoroutines)

    var (
        // Espera para grupo de goroutines finalizar
        wg sync.WaitGroup
        // Propriade
        mu sync.Mutex
        // Lista para armazenar inteiros
        list []int
    )

    // Loop com mihão de indices
    for i := 0; i 



benchmarking_without_enhancement_test.go:

package main

import (
    "runtime"
    "sync"
    "testing"
)

// BenchmarkWithoutEnhancement Função sem a melhoria, para adicionar o indice do loop em um array de inteiro
func BenchmarkWithoutEnhancement(b *testing.B) {
    // Obtém o número de CPUs disponíveis
    numCPUs := runtime.NumCPU()
    // Define o máximo de CPUs para serem usadas pelo programa
    maxGoroutines := runtime.GOMAXPROCS(numCPUs)
    // Criação do semáforo
    semaphore := make(chan struct{}, maxGoroutines)

    var (
        // Espera para grupo de goroutines finalizar
        wg sync.WaitGroup
        // Propriade
        mu sync.Mutex
        // Lista para armazenar inteiros
        list []int
    )

    // Loop com mihão de indices
    for i := 0; i 



Разница между ними в том, что используется импорт библиотеки Uber.

При запуске теста, предполагая, что будут использоваться 2 процессора, результат был следующим:

Golang: Como a observabilidade e profiling revelaram um throttling quase indetectável

ns/op: показывает среднее время в наносекундах, необходимое для выполнения определенной операции.

Обратите внимание, что общее количество доступных процессоров составляет 8 ядер, и именно это возвращает свойство runtime.NumCPU(). Однако, как и при запуске теста, я определил, что будут использоваться только два процессора, а файл, который не использовал automaxprocs, определил, что предел выполнения за раз будет 8 горутин, а наиболее эффективным будет 2, потому что таким образом, использование меньшего количества ресурсов делает выполнение более эффективным.

Итак, важность наблюдения и профилирования наших приложений очевидна.

Заявление о выпуске Эта статья перепечатана по адресу: https://dev.to/mggcmatheus/golang-como-a-observabilidade-e-profiling-reelaram-um-throttling-quase-indetectavel-1h5p?
Последний учебник Более>

Изучайте китайский

Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.

Copyright© 2022 湘ICP备2022001581号-3