Последовательный (или линейный) поиск - наверное одна из самых простых и наиболее использумых программистких задач, выполняющая повсеместно почти что в любой программе. Казалось бы - что вообще можно оптимизировать в таком элементарном алгоритме?
Оказывается и здесь есть достаточное поле для улучшения производительности...
По книге Джона Бентли:
"Жемчужины программирования"
"...Обратимся к последовательному поиску в таблице, которая может быть неотсортированной:
int ssearch1(t) for i = 0 to n if x[i] == t return i return -1;
Эта короткая программа находит элемент в массиве х за 4,06n нc в среднем (прим. редактора: при быстроте компьютеров по тем временам конечно). Поскольку в среднем приходится просматривать примерно половину элементов, на каждый из них приходится около 8,1 нc.
Цикл выглядит достаточно стройным, но жирок с него все-таки срезать можно. Во внутреннем цикле выполняется две проверки. Сначала проверяется, достиг ли индекс i конца массива, а затем выясняется, не равен ли элемент x[i] искомому. Первую проверку можно исключить, добавив элемент-метку в конце массива:
int ssearch2(t) hold = x[n] x[n] = t for (i = 0; ;i++) if x[i] == t break x[n] = hold if i == n return -1 else return i;
Время работы уменьшилось до 3,87n нc, то есть быстродействие возросло на 5%. Мы предполагаем, что массив уже выделен, поэтому имеется возможность временно затереть значение х[n]. Нужно быть аккуратным при сохранении значения х[n] и его восстановлении. В большинстве приложений этого не требуется, так что мы исключим эту операцию из следующей версии программы.
Теперь внутренний цикл содержит только операцию увеличения индекса, обращение к элементу массива и проверку. Можно ли еще что-нибудь урезать? В последней версии нашей программы количество повторов цикла сокращается в 8 раз. Дальнейшее сокращение не ускорило работу программы.
В итоге получаем такой последовательный поиск в массиве, оптимизированный практически до предела:
int ssearch3(t) x[n] = t for (i = 0; ;i += 8) if x[i] == t break if x[i+1] == t i += 1 break if x[i+2] == t i += 2 break if x[i+3] == t i += 3 break if x[i+4] == t i += 4 break if x[i+5] == t i += 5 break if x[i+6] == t i += 6 break if x[i+7] == t i += 7 break if i == n return -1 else return i;
При этом время поиска сокращается до 1,07n нc, то есть наблюдается 56% ускорение. На старых компьютерах уменьшение дополнительных затрат может дать ускорение работы на 10 или 20%. На современных машинах раскрытие цикла позволяет избежать остановки конвейера, уменьшить количество ветвей и увеличить параллелизм на уровне инструкций.... "
При этом согласно проведенным примерным тестам производительности - оптимизация #1 быстрее базовой на процентов 10%, оптимизация #2 - процентов на 20%.