Conversation
3816±188 → 3458±13
3458±13 → 2317±18
2317±18 → 1756±66
|
Круто! Да, hand blockers один из первых кандидатов, но я не стал менять, т.к. в версии с соревнования тоже хэшмап. Матчить кстати можно чисто по y, x всегда == 1. Про Link-Time Optimizer не знал, спасибо! |
|
@tonsky во, я как раз думал "а что собственно иллюстрирует этот мой пулл-реквест". Забавно что меня так и подмывало переписать (Что забавно, если бы я писал на Руби, мне пожертвовать подобной семантикой в пользу "машинности" наверняка и в голову бы не пришло.) |
Меня тоже! В основном из-за удобного конструктора и деструктуринга, на самом деле. Но потом я понял что в коде вместо |
|
Коллекции из стандартной библиотеки по-умолчанию используют криптографически стойкий хэш, можно использовать что-нибудь пошустрее чтобы выжать ещё немного производительности (например, https://lib.rs/crates/fnv). |
|
Замержил в мастер, немножко поменяв порядок. Получается так: LTO дало совсем чуть-чуть, FNV дало прирост в 1.8 при применении к старому hand_blockers на HashMaps. После FNV смена hand_blockers на вектор дала еще 15%. |
По следам https://tonsky.livejournal.com/322450.html?thread=5466770#t5466770
У меня выдалось свободных полчаса, и я посмотрел, нет ли тупого способа приподускорить этот код.
Получилось вдвое, с 3816±188 до 1756±66 мсек (на конкретно моём ноуте с линуксом, мерял по выбранной наобум задаче 272).
По шагам (каждый шаг — один коммит):
Я тупо включил Link-Time Optimizer. Минус десять процентов. Пришлось пожертвовать скоростью компиляции (принудительно сделав один codegen-unit), так линкер лучше видит глобальную картинку и оптимальнее оптимизирует. Также заменил паники на abort, это тоже должно быть побыстрее, а нам всё равно не нужен бектрейс в релизе.
Окей, прогнал Callgrind, увидел что дороже всего обходится хешинг при чтении из
HAND_BLOCKERS. Заменил его втупую на матчинг, сократил время пробега с 3.4 до 2.3 секунды. (Хеш-мапы — дорогие, потому что прогоняют хеширование у ключа на каждое чтение и запись. Если можно заменить на цепочки ифов — стоит заменить на цепочку ифов).Тут я считерил, и заменил код из lazy_static на вручную прописанные вектора, которые сгенерил скриптом на руби по изначальному (вроде бы) алгоритму. Если где-то и закралась ошибка — то именно тут. На измерения перформанса она точно не влияет.
Так, но теперь мы аллоцируем эти вектора на каждый чих — это тоже так себе. Выносим их в статики; можно бы и вернуть обратно их алгоритмический рассчёт, но лень (а на перформанс не влияет). Ещё минус 20%, 1.7 секунды в итоге.
Вот. Следующий шаг будет — заменить хешмэп в
would_wrapна двумерный битмап. Да, будет дофига нулей в большущем двумерном массиве. Но скорее всего там не такие большие размеры, чтобы эта память чего-нибудь стоила — но по Callgrind это опять узкое место, и опять дело в хешировании для HashSet.Кажется, что ещё за 2-4 итерации ещё раза 2-3 будет легко выиграть.
Всё это я делал по-прежнему не вникая в детали алгоритма, чтобы не начать случайно алгоритмически оптимизировать.