?

Log in

Бесполезное невероятное - A cure for !being an axe-wielding homicidal maniac [entries|archive|friends|userinfo]
A cure for !being an axe-wielding homicidal maniac

[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

Бесполезное невероятное [янв. 9, 2015|01:32 pm]
A cure for !being an axe-wielding homicidal maniac
Я потратил несколько дней на странное — писал валидацию UTF-8 в С++, с бенчмарком и тестами (линк).

В общем условия задачи такие — есть std::string с какими-то данными, которые прикидываются UTF-8. Нужно данные скопировать в новый std::string. Невалидные куски надо закодировать определенным образом, валидные — скопировать как есть.

Я написал несколько вариантов: с записью результатов в «бесконечный буфер» (без проверки границ и логики наращивания буфера; быстрее вряд ли будет), с записью в растущий буфер, реализованный вручную на базе malloc ну и третий вариант из условий задачи.

Внезапно оказалось, что вариант на базе std::string ощутимо медленнее.

Проблема видимо в том, insert() содержит весьма нетривиальную логику, причем у меня в стандартной либе эта функция реализована не очень удачно — у компилятора есть выбор инлайнить лишнее или не инлайнить вообще, он предпочитает заинлайнить лишнее.

Вызовов insert() аж 6, для каждой ветки кода (1-байтная, 2-байтная и тд. UTF-8 последовательность).

В общем я теперь наращиваю строку кусками по 128 байт и пишу результат через указатели, получается довольно быстро. То есть сам string рассматривается как еще один аллокатор, структурно код похож на вариант с malloc — код для наращивания буфера существует в единственном экземпляре, внутри ветки которая исполняется редко, поэтому насколько там внутри все неоптимально — значения уже не имеет.

Честно говоря, результат меня удивил. Выглядит честно говоря по-уродски, но работает вроде ок.


PS. попробовал Stack Overflow, впечатления смешанные.
СсылкаОтветить

Comments:
From: sleepy_drago
2015-01-10 12:56 pm
это походу я туплю.

в чем смысл претензий: ты написал код, который одинаково хорошо (по виду) обрабатывает любые сложные случаи во входных данных.

с точки зрения скорости бывают 2 вида запросов "минимум общего времени в среднем на большом числе запросов", "максимальное время затрачиваемое на вычисление 1го ответа в мс".

В первом случае выбирается (меряется экспериментально например) 1 распределение входных данных и код делится на 2 части. назовем их hot/cold. В хорошем раскладе Hot маленький, простой и накрывает большинство случаев. его обнаружение и выполнение и раскладку в памяти детально делают руками. Он дает скорость. В идеале остальная часть, которая содержит обработку ошибок, суперсложные случаи, и тп. в процессор попадает только когда такой случай таки произошел. Здесь упор делается только на корректность, тут можно копировать строки, кидать исключения и тп.

Если у тебя запросы 2го вида (реалтайм, секьюрити, игры, и тп.) то это другая история со своими правилами. я про нее не имел ничего ввиду выше.
(Ответить) (Parent) (Thread)