Стимулировать встраивание кода
В отличие от таких языков, как C++, где вы можете явно указать inline
в Go компилятор динамически обнаруживает функции, которые являются кандидатами для встраивания (что может делать и C++, но Go не может делать и то, и другое). Также есть опция отладки, чтобы увидеть возможное встраивание, но в Интернете очень мало документированных о точной логике компиляторов go, делающих это.
Допустим, мне нужно повторно запускать большой цикл для набора данных каждый n-период;
func Encrypt(password []byte) ([]byte, error) {
return bcrypt.GenerateFromPassword(password, 13)
}
for id, data := range someDataSet {
newPassword, _ := Encrypt([]byte("generatedSomething"))
data["password"] = newPassword
someSaveCall(id, data)
}
Нацелен, например, на Encrypt
чтобы правильно указать, какую логику нужно учитывать компилятору?
Я знаю из C++, что передача по ссылке увеличит вероятность автоматического встраивания без явного inline
ключевое слово, но не очень легко понять, что именно делает компилятор, чтобы определить решения о выборе встроенного или нет в Go. Языковые скрипты, такие как, например, PHP, сильно страдают, если вы делаете цикл с константой addSomething($a, $b)
где сравнение такого миллиарда циклов стоимость его по сравнению с $a + $b
(встроенный) почти смешно.
3 ответа
Цитирование minux (на 2013-01-31):
По умолчанию инлайнер пытается встроить конечную функцию (не вызывает другие функции / метод / интерфейсы), которая не вызывает панику, не восстанавливает, не выбирает, не переключает, не создает функции закрытия или перехода / откладывания (см. Пример ниже) и которая меньше 40 узлов, если представлено (примерно соответствует 40 простым операциям). Но, пожалуйста, имейте в виду, что это описывает только текущий статус-кво компилятора gc, и он, несомненно, улучшится в будущем. Поэтому, пожалуйста, постарайтесь не зависеть от этого.
Пока у вас не возникнут проблемы с производительностью, вам все равно. Встроенный или нет, он будет делать то же самое.
Если производительность имеет значение и имеет заметное и существенное значение, то не полагайтесь на текущие (или прошлые) условия встраивания, "встроите" ее в себя (не помещайте ее в отдельную функцию).
Правила можно найти в $GOROOT/src/cmd/compile/internal/gc/inl.go
файл. Вы можете контролировать его агрессивность с 'l'
флаг отладки.
// The inlining facility makes 2 passes: first caninl determines which
// functions are suitable for inlining, and for those that are it
// saves a copy of the body. Then inlcalls walks each function body to
// expand calls to inlinable functions.
//
// The debug['l'] flag controls the aggressiveness. Note that main() swaps level 0 and 1,
// making 1 the default and -l disable. -ll and more is useful to flush out bugs.
// These additional levels (beyond -l) may be buggy and are not supported.
// 0: disabled
// 1: 40-nodes leaf functions, oneliners, lazy typechecking (default)
// 2: early typechecking of all imported bodies
// 3: allow variadic functions
// 4: allow non-leaf functions , (breaks runtime.Caller)
//
// At some point this may get another default and become switch-offable with -N.
//
// The debug['m'] flag enables diagnostic output. a single -m is useful for verifying
// which calls get inlined or not, more is for debugging, and may go away at any point.
Также ознакомьтесь с сообщением в блоге: Dave Cheney - Пять вещей, которые делают Go fast (2014-06-07), в котором говорится о встраивании (длинный пост, он примерно посередине, ищите слово "inline").
Также интересная дискуссия об улучшениях встраивания (может быть, Go 1.9?): Cmd/compile: улучшить модель стоимости встраивания #17566
Еще лучше, не угадай, измерь! Вы должны доверять компилятору и избегать попыток угадать его внутреннюю работу, так как он будет меняться от одной версии к другой. Компилятор, процессор или кэш могут сыграть слишком много хитростей, чтобы предсказать производительность по исходному коду.
Что, если встраивание делает ваш код больше до такой степени, что он больше не помещается в строку кэша, делая его намного медленнее, чем не встроенная версия? Локальность кэша может оказать гораздо большее влияние на производительность, чем ветвление.
Вы ведете тяжелую битву. Go не создан для того, что вы пытаетесь сделать. Go не поддается настройке и рассчитан на среднюю производительность. Он ценит простоту выше производительности, поэтому людям не следует использовать его там, где вам нужно более точное поведение, например, при встраивании. Языки, которые больше ценят производительность, имеют API для встраивания. Посмотрите Rust, C++, C#.