Рубрики
Uncategorized

Отложенный переход будет страдать от снижения производительности. Не используйте его как можно чаще?

Автор оригинала: David Wong.

Адрес: отложенный переход приведет к потере производительности, попробуйте не использовать его?

В прошлом месяце в группе @ [email protected] технология стека лезвий я видел, как небольшой партнер спросил “Если задержка выполняется при выходе из стека, произойдет снижение производительности. Не используйте отсрочку как можно чаще. Каково же решение?”

Совсем недавно я написал статью “Глубокое понимание откладывается”, чтобы подробно проанализировать ее. отложить Ключевое слово. Поэтому на этот раз я хочу обсудить этот вопрос простым способом. Я надеюсь, что это будет полезно для вас. Но перед этим, я надеюсь, вы сможете потратить несколько минут на обдумывание ответа для себя, а затем продолжить смотреть вниз.

тест

func DoDefer(key, value string) {
    defer func(key, value string) {
        _ = key + value
    }(key, value)
}

func DoNotDefer(key, value string) {
    _ = key + value
}

Тестирование:

func BenchmarkDoDefer(b *testing.B) {
    for i := 0; i < b.N; i++ {
        DoDefer ("fried fish", "https://github.com/EDDYCJY/blog")
    }
}

func BenchmarkDoNotDefer(b *testing.B) {
    for i := 0; i < b.N; i++ {
        DoNotDefer ("fried fish", "https://github.com/EDDYCJY/blog")
    }
}

Выходные результаты:

$ go test -bench=. -benchmem -run=none
goos: darwin
goarch: amd64
pkg: github.com/EDDYCJY/awesomeDefer
BenchmarkDoDefer-4          20000000            91.4 ns/op          48 B/op           1 allocs/op
BenchmarkDoNotDefer-4       30000000            41.6 ns/op          48 B/op           1 allocs/op
PASS
ok      github.com/EDDYCJY/awesomeDefer    3.234s

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

Подумайте об этом.

$ go tool compile -S main.go 
"".main STEXT size=163 args=0x0 locals=0x40
    ...
    0x0059 00089 (main.go:6)    MOVQ    AX, 16(SP)
    0x005e 00094 (main.go:6)    MOVQ    $1, 24(SP)
    0x0067 00103 (main.go:6)    MOVQ    $1, 32(SP)
    0x0070 00112 (main.go:6)    CALL    runtime.deferproc(SB)
    0x0075 00117 (main.go:6)    TESTL    AX, AX
    0x0077 00119 (main.go:6)    JNE    137
    0x0079 00121 (main.go:7)    XCHGL    AX, AX
    0x007a 00122 (main.go:7)    CALL    runtime.deferreturn(SB)
    0x007f 00127 (main.go:7)    MOVQ    56(SP), BP
    0x0084 00132 (main.go:7)    ADDQ    $64, SP
    0x0088 00136 (main.go:7)    RET
    0x0089 00137 (main.go:6)    XCHGL    AX, AX
    0x008a 00138 (main.go:6)    CALL    runtime.deferreturn(SB)
    0x008f 00143 (main.go:6)    MOVQ    56(SP), BP
    0x0094 00148 (main.go:6)    ADDQ    $64, SP
    0x0098 00152 (main.go:6)    RET
    ...

Мы уже упоминали выше. отложить Ключевое слово на самом деле включает в себя серию последовательных вызовов, внутренних во время выполнения Для вызова функции есть еще как минимум три шага. runtime.deferproc Один раз и runtime.deferr Дважды.

Это только явное действие во время выполнения, и компилятор выполняет множество действий, таких как:

  • пребывание отсрочка Фаза (вызов задержки регистрации), также необходимо получить/передать адрес целевой функции, параметры функции и так далее.
  • остаться отсрочка В конце вызова функции необходимо вставить вызов метода, и если он вызван, вызов метода должен быть вставлен в конце вызова функции. отложить Функция runtime·jmpdefer Выполняет переход для облегчения последующих вызовов.

В этих действиях участвуют самые маленькие подразделения на этом пути. _defer Получение/генерация отсрочка и восстановление Логическая обработка и использование связанного списка.

Вопросы и ответы

Об этом было упомянуто в конце обсуждения. “Проблема в том, что он предназначен для выполнения операций close (), а затем пытается не использовать его. В примере удаляется функция отложить до того, как отложить db. close ().” Этот вопрос.

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

resp, err := http.Get(...)
if err != nil {
    return err
}
defer resp.Body.Close()

Но обязательно ли вам это писать? На самом деле это неправда. Многие люди приводят причины для того, чтобы сказать, что они боятся забыть. В этом нет ничего плохого. Но вам нужно распознать сценарий, предполагая, что сценарий моего приложения выглядит следующим образом:

resp, err := http.Get(...)
if err != nil {
    return err
}
defer resp.Body.Close()
// do something
time.Sleep(time.Second * 60)

Ну, конечно, просьба-это не проблема. Трафик и параллелизм внезапно увеличиваются. Это может обернуться катастрофой. Почему вы думаете об этом? Из общей комбинации отложить + закрыть С точки зрения использования рекомендуется четко просмотреть сценарий приложения перед его использованием и убедиться, что он будет закрыт как можно скорее без исключений, это первый выбор. Если это всего лишь небольшой звонок, который скоро вернется, нетрудно полениться и выйти с комбинацией ударов.

заключение

Одно ключевое слово defer на самом деле содержит множество действий и обработки, и вы просто называете функцию несопоставимой инструкцией. По сравнению с объектом управления это действительно потеря производительности. В настоящее время общая стоимость отложенного вызова составляет около 50 нс, но отложить Предоставленная роль гораздо больше, чем это. С общей точки зрения, его потери очень малы, и правительство постоянно проводит оптимизацию.

Поэтому для “Go defer будет иметь потерю производительности, попробуйте не использовать его?” Этот вопрос, я думаю Он должен быть выключен вовремя, без промедления. При использовании горячих путей вы должны четко продумать сценарий.

дополнение

Добавьте ответ Хайды: “Это не проблема с производительностью. Самая большая функция отсрочки заключается в том, что она все еще работает после паники. Без отсрочки паника приведет к потере разблокировки, что приведет к тупику. Это очень классично.