Horspool, KMP и др. Оптимальны для минимизации количества сравнений байтов.
Однако это не узкое место на современном процессоре. На процессоре x86 / 64 ваша строка загружается в кэш L1 кусками ширины строки кэша (обычно 64 байта). Независимо от того, насколько умный ваш алгоритм, если он не дает вам больше шагов, вы ничего не получите; и более сложный код Horspool (по крайней мере, один просмотр таблицы) не может конкурировать.
Кроме того, вы застряли со строковым ограничением "C" нулевого завершения: КУДА-ТО, где код должен проверять каждый байт.
strstr()
, как ожидается, будет оптимальным для широкого диапазона случаев; например поиск крошечных строк, таких как "\r\n"
, в короткой строке, а также гораздо более длинных строк, в которых есть надежда на более умный алгоритм. Базовый цикл strchr / memcmp довольно сложно превзойти по всему диапазону возможных входных данных.
Практически все x86-совместимые процессоры с 2003 года поддерживают SSE2. Если вы разобрали strlen()
/ x86 для glibc , вы могли заметить, что он использует некоторые операции SSE2 PCMPEQ и MOVMASK для поиска нулевого терминатора 16 байт за раз. Решение настолько эффективно, что превосходит очевидный супер-простой цикл, для чего-либо длиннее пустой строки.
Я взял эту идею и придумал strstr()
, который превосходит strstr()
у glibc для всех случаев, превышающих 1 байт - где относительная разница в значительной степени спорная. Если вы заинтересованы, проверьте:
Конвергенция SSE2 и strstr()
лучше strstr()
без кода ASM
Если вы хотите увидеть решение, отличное от SSE2, которое доминирует в strstr()
для целевых строк длиной более 15 байтов, проверьте:
, который использует многобайтовые сравнения вместо strchr()
, чтобы найти точку, в которой нужно выполнить memcmp.
Кстати, вы, наверное, уже поняли, что операции x86 REP SCASB / REP CMPSB выпадают на задницу дольше, чем 32 байта, и не намного лучше для более коротких строк. Хотелось бы, чтобы Intel уделил этому немного больше внимания, чем добавлению SSE4.2 "string" ops.
Для достаточно больших струн мои тесты показывают, что BNDM лучше, чем Horspool, по всем направлениям. BNDM более терпим к «патологическим» случаям, таким как цели, которые сильно повторяют последний байт шаблона. BNDM также может использовать SSE2 (128-битные регистры) таким образом, чтобы конкурировать с 32-битными регистрами по эффективности и стоимости запуска. Исходный код здесь .