Автор перешёл на голанг, теперь буду писать заметки конкретно об этом языке.
Итак, с чего бы хотелось интересного начать (точнее, что у меня сейчас на уме) - loop unrolling. По сути, базовая оптимизация, когда вместо цикла мы просто много раз повторяем инструкции. Это пошло ещё с древних времён, почему циклы работают медленней развёрнутых - я без понятия. В голанге, к слову, тоже присутствует такая проблема, так как каждая итерация занимает ровно 2 наносекунды, что порой может быть критичным
Чтобы упростить дальнейшее понимание данной штуки - приведу пример на Го:
data := "Hello, world!"Данный код выведет построчно
for _, char := range data {
fmt.Println(char)
}
Hello, world!. При развёртывании же цикла, мы будем иметь нечто вроде:fmt.Println("H")
fmt.Println("e")
fmt.Println("l")
fmt.Println("l")
fmt.Println("o")
...
И это даст нам несомненный прирост производительности! Однако же, как вы могли бы заметить, данный пример работает ой как не всегда - нужно знать конкретную длину входных данных. Тем не менее, мы можем поступить проще, сделав нечто на подобииdata := "Hello, world!"Что мы здесь видим? Мы совершаем в ~4 раза меньше итераций, поскольку вместо 4 итераций, мы делаем 4 операции в одной. Можно выставить как 4, так и 6, и 8, и вообще 20 как шаг. Единственное - рекомендую выставлять число, равное степени двойки.
i := 0
dataLen := len(data)
for {
fmt.Println(data[i])
fmt.Printlnb(data[i+1])
fmt.Println(data[i+2])
fmt.Println(data[i+3])
i += 4
if dataLen-i < 4 {
break
}
}
for i < dataLen {
fmt.Println(data[i])
i++
}
Что по поводу выигрыша - у меня он составил около 35% при шаге в 8 и строке длиною 5,000 элементов. Что, я считаю, очень даже весомо, особенно в ситуациях, когда входных данных достаточно много (экспериментальным путём я пока не успел выяснить, при насколько малых объемах выигрыш становится не столь существенным). Да, это усугубляет читаемость. Да, из-за неё у нас появляется копипаста кода. Да, в некоторых случаях это может превратиться в кучу одинакового кода, если результат каждой операции нужно как-то обрабатывать (к примеру - обрабатывать возврат ошибки функции). И тем не менее, быстрый код - зачастую противоположность красоте, посему не рекомендую городить такие штуки в коде, в котором не настолько важна производительность.
Подводя итоги, хочу сказать, что штука действительно ситуативная. Использовать, или нет - дело сугубо каждое. Посему - спешу откланяться и поблагодарить того, кто прочитал всю эту вероятно бесполезную простыню больного шизофренией маньяка оптимизаций