strstr быстрее, чем алгоритмы?
у меня есть файл, 21056 байт.
Я написал программу на C, которая считывает весь файл в буфер, а затем использует несколько алгоритмов поиска для поиска файла для токена, который составляет 82 символа.
я использовал все реализации алгоритмов с "Точные Алгоритмы Сопоставления Строк"
4 ответов
почему вы думаете strstr
должен быть медленнее, чем все остальные? Вы знаете, какой алгоритм strstr
использует? Я думаю, вполне вероятно, что strstr
использует тонко настроенный, специфичный для процессора, сборочно-кодированный алгоритм KMP
тип или лучше. В этом случае у вас нет шансов превзойти его в C
для таких маленьких показателей.
(причина, по которой я думаю, что это вероятно, в том, что программисты любят реализовывать такие вещи.)
Horspool, KMP и др. являются оптимальными при минимизации количества байт-сравнений.
однако это не узкое место на современном процессоре. На процессоре x86/64 ваша строка загружается в Л1 кэша в кусках ширины строки кэша (обычно 64 байта). Независимо от того, насколько умен ваш алгоритм, если он не дает вам шагов, которые больше, вы ничего не получите; и более сложный код Horspool (по крайней мере, один поиск таблицы) не может конкурировать.
кроме того, вы застряли с ограничением строки "C" null-termination: где-то код должен исследовать каждый байт.
strstr()
, Как ожидается, будет оптимальным для широкого спектра случаев; например, поиск крошечных строк, таких как "\r\n"
в короткой строке, а также гораздо более длинные, где какой-то более умный алгоритм может иметь надежду. Базовый цикл strchr/memcmp довольно трудно превзойти во всем диапазоне вероятных входов.
почти все x86-совместимые процессоры с 2003 года поддерживают SSE2. Если вы разобрали strlen()
/x86 для glibc, вы, возможно, заметили, что он использует некоторые операции SSE2 PCMPEQ и MOVMASK для поиска нулевого Терминатора 16 байтов за раз. Решение настолько эффективно, что оно превосходит очевидный супер-простой цикл для чего-либо длиннее пустой строки.
Я принял эту идею и придумал strstr()
что бьется в glibc strstr()
для всех случаев более 1 байта --- где относительная разница довольно спорна. Если вам интересно, проверьте:
-
если вы хотите увидеть не-SSE2 решение, которое доминирует
strstr()
для целевых строк более 15 байт, проверьте:который использует многобайтовые сравнения, а не
strchr()
, чтобы найти точку, в которой сделать memcmp.
кстати, вы, вероятно, уже поняли, что x86 REP SCASB / REP CMPSB ops падают на их задницу для чего-либо длиннее 32 байтов и не намного улучшаются для более коротких строк. Жаль, что Intel уделила немного больше внимания этому, чем добавлению SSE4.2" string " ops.
для строк, достаточно больших, чтобы иметь значение, мои тесты perf показывают, что BNDM лучше, чем Horspool, по всем направлениям. BNDM более терпим к "патологическим" случаям, например, цели, которые сильно повторяют последний байт шаблона. BNDM может также использовать SSE2 (128-битные регистры) таким образом, чтобы конкурировать с 32-битными регистрами по эффективности и стоимости запуска. Исходный код здесь.
не видя ваш код, трудно сказать точно. strstr
сильно оптимизирован и обычно написан на языке ассемблера. Он делает такие вещи, как чтение данных 4 байта за раз и сравнение их (бит-twiddling, если необходимо, если выравнивание неправильное), чтобы минимизировать задержку памяти. Он также может использовать такие вещи, как SSE, для загрузки 16 байтов за раз. Если ваш код загружает только один байт за раз, он, вероятно, погибает из-за задержки памяти.
использовать отладчик и шаг через разборку strstr
-- вы, вероятно, найдете там интересные вещи.
представьте, что вы хотите что-то очистить. Вы можете просто почистить его сами или нанять десять профессиональных уборщиков. Если работа по уборке является офисным зданием, последнее решение было бы предпочтительнее. Если бы работа по уборке была одним окном, первое было бы предпочтительнее.
вы никогда не получите никакой окупаемости за время, потраченное на настройку, чтобы сделать работу эффективно, потому что работа не занимает много времени.