Skip to content

Немного туповатая оптимизация#1

Closed
kirushik wants to merge 3 commits intotonsky:masterfrom
kirushik:master
Closed

Немного туповатая оптимизация#1
kirushik wants to merge 3 commits intotonsky:masterfrom
kirushik:master

Conversation

@kirushik
Copy link
Contributor

@kirushik kirushik commented Jul 9, 2019

По следам https://tonsky.livejournal.com/322450.html?thread=5466770#t5466770

У меня выдалось свободных полчаса, и я посмотрел, нет ли тупого способа приподускорить этот код.
Получилось вдвое, с 3816±188 до 1756±66 мсек (на конкретно моём ноуте с линуксом, мерял по выбранной наобум задаче 272).

По шагам (каждый шаг — один коммит):

  1. Я тупо включил Link-Time Optimizer. Минус десять процентов. Пришлось пожертвовать скоростью компиляции (принудительно сделав один codegen-unit), так линкер лучше видит глобальную картинку и оптимальнее оптимизирует. Также заменил паники на abort, это тоже должно быть побыстрее, а нам всё равно не нужен бектрейс в релизе.

  2. Окей, прогнал Callgrind, увидел что дороже всего обходится хешинг при чтении из HAND_BLOCKERS. Заменил его втупую на матчинг, сократил время пробега с 3.4 до 2.3 секунды. (Хеш-мапы — дорогие, потому что прогоняют хеширование у ключа на каждое чтение и запись. Если можно заменить на цепочки ифов — стоит заменить на цепочку ифов).
    Тут я считерил, и заменил код из lazy_static на вручную прописанные вектора, которые сгенерил скриптом на руби по изначальному (вроде бы) алгоритму. Если где-то и закралась ошибка — то именно тут. На измерения перформанса она точно не влияет.

  3. Так, но теперь мы аллоцируем эти вектора на каждый чих — это тоже так себе. Выносим их в статики; можно бы и вернуть обратно их алгоритмический рассчёт, но лень (а на перформанс не влияет). Ещё минус 20%, 1.7 секунды в итоге.

Вот. Следующий шаг будет — заменить хешмэп в would_wrap на двумерный битмап. Да, будет дофига нулей в большущем двумерном массиве. Но скорее всего там не такие большие размеры, чтобы эта память чего-нибудь стоила — но по Callgrind это опять узкое место, и опять дело в хешировании для HashSet.

Кажется, что ещё за 2-4 итерации ещё раза 2-3 будет легко выиграть.
Всё это я делал по-прежнему не вникая в детали алгоритма, чтобы не начать случайно алгоритмически оптимизировать.

Kirill Pimenov added 3 commits July 9, 2019 18:02
3816±188 → 3458±13
3458±13 → 2317±18
2317±18 → 1756±66
@tonsky
Copy link
Owner

tonsky commented Jul 9, 2019

Круто! Да, hand blockers один из первых кандидатов, но я не стал менять, т.к. в версии с соревнования тоже хэшмап. Матчить кстати можно чисто по y, x всегда == 1.

Про Link-Time Optimizer не знал, спасибо!

@kirushik
Copy link
Contributor Author

@tonsky во, я как раз думал "а что собственно иллюстрирует этот мой пулл-реквест".
И кажется что именно это: при разработке на Кложе удобно думать в математических понятиях (а хешмэп это пожалуй самая близкая к функции структура данных).
При разработке на Расте это кажется прямо-таки немного противоестственным, и хочется уже взять плоский кусок памяти, и как-то там его расчерчивать. Это своеобразное отражение того, как компьютеры думают про программы, и людям приходится приноравливаться, чтобы выжимать побольше перформанса, адаптируя процесс мышления к тому, как собственно на практике работают процессоры. (Те самые вопросы на интервью типа "вот вам односвязный список, найдите, есть ли в нём петля" — проверяют ровно это окомпьютеренное мышление, и достались нам из эпохи, когда никакого другого программирования в общем-то и не было.)

Забавно что меня так и подмывало переписать Point на тьюплы (isize, isize), потому что ну это же просто пара чисел одинакового типа, зачем огород городить с именованными полями?
Это вообще никак не повлияло бы на производительность, и скорее всего машинный код, в который это собирается компилятором, вообще бы не поменялся.

(Что забавно, если бы я писал на Руби, мне пожертвовать подобной семантикой в пользу "машинности" наверняка и в голову бы не пришло.)

@tonsky
Copy link
Owner

tonsky commented Jul 10, 2019

Забавно что меня так и подмывало переписать Point на тьюплы (isize, isize)

Меня тоже! В основном из-за удобного конструктора и деструктуринга, на самом деле. Но потом я понял что в коде вместо .y будет .1 и стало ясно что это неверный путь. Тем более что по скорости — я надеюсь — разницы нет

@kirushik kirushik closed this Jul 10, 2019
@ul
Copy link

ul commented Jul 11, 2019

Коллекции из стандартной библиотеки по-умолчанию используют криптографически стойкий хэш, можно использовать что-нибудь пошустрее чтобы выжать ещё немного производительности (например, https://lib.rs/crates/fnv).

@tonsky
Copy link
Owner

tonsky commented Jul 11, 2019

Замержил в мастер, немножко поменяв порядок. Получается так: LTO дало совсем чуть-чуть, FNV дало прирост в 1.8 при применении к старому hand_blockers на HashMaps. После FNV смена hand_blockers на вектор дала еще 15%.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants