UTF-8: как быстро получить подстроку (+новый вариант strlen)
Из кода быстрой функции strlen
Я вкомпилил эту функцию в PHP, помог найти ошибки и отладить и сделал замеры:
bolk-dev ~/uni/futf-0.1 $ php ../test/test_substr.php | sort -k2 -t:
Fast UTF-8: 0.18010711669922
mb_substr: 0.87068700790405Разница примерно в 4 раза. Я ещё попробовал отрезать строку через функцию preg_match, сравнимо по скорости с mb_substr, но медленнее и есть ограничение — квантификатор preg_match не захватывает больше 65535 символов.
Добавлено позднее: после того как мы отладили эту функцию, я вспомнил про команды ассемблера SSE — это наборы команд (сейчас их пять — SSE1, 2, 3, 4.1 и 4.2), которые оперируют восемью регистрами размером 128 бит.
Как я рассказывал, самый быстрый алгоритм для strlen, который я нашёл, работает следующим образом: сначала указатель на строку первым циклом выравнивается по границе 4 или 8 байт (в зависимости от архитектуры процессора), чтобы процессор смог быстрее читать дальше, потом, работая сразу с числом длиной 4 или 8 байт, одной операцией подсчитывается сколько в выделенной строке содержится начальных байт символов.
Если внутри нашего рабочего участка встречается символ с кодом ноль (признак конца строки), то происходит выход в последний цикл, который занимается тем, что просматривает побайтно получившийся «хвост».
Час назад я поставил себе gcc под Windows и стал потихоньку писать примерный код, читать документацию, изучать команды в интернете, пока совершенно случайно не наткнулся на уже готовый вариант, написанный с применением инструкций SSE2.
Нашёл в интернете тест для таких функций, включил туда найденную и сделал замеры:
C:\MinGW\bin\test.exe
cp_strlen_utf8: 0.105706
cp_strlen_utf8_sse2: 0.030002В 3,5 раза обгоняет предыдущий вариант, то есть, смею надеяться, обгонит встроенную в PHP функцию примерно в 24 раза. Завтра попробую.
Немаловажно, что даже на машинах с архитектурой 32 бита рассматриваемый вариант покажет ту же скорость, что и на машинах 64 бита.
Комментарии 11
квантификатор (от «квант», полагаю)
Судя по бенчмаркам обгоняет все существующие аналогичные алгоритмы
болк, а ты маниак! переводил вику на утф, а уже почти на ассемблере кодишь :)
Комментарий для Евгения Степанищева:
Я не очень понял что ты имеешь в виду. Возвращается адрес найденной подстроки или если не найдена — адрес последнего байта+1. Так как в std::search(). А тебе надо индекс?
Если найдена:
индекс = результат — адрес строки (в которой ищеться)
А какие строки PHP использует?
Мне интересно что оказалось быстрее: мой алгоритм или SSE?
Комментарий для Leonid Volnitsky (http://volnitsky.com):
Что? О чём вы?
Нет. Но в любом случае, от реализации языка спрятана реализация строк.
Да какойалгоритм-то? Вы знаете как устроен UTF-8?