Адрес: отложенный переход приведет к потере производительности, попробуйте не использовать его?
В прошлом месяце в группе @ [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 будет иметь потерю производительности, попробуйте не использовать его?” Этот вопрос, я думаю Он должен быть выключен вовремя, без промедления. При использовании горячих путей вы должны четко продумать сценарий. 。
дополнение
Добавьте ответ Хайды: “Это не проблема с производительностью. Самая большая функция отсрочки заключается в том, что она все еще работает после паники. Без отсрочки паника приведет к потере разблокировки, что приведет к тупику. Это очень классично.