Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni
🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!13.03.2024
1BRC на Ruby: нарешті, правильна паралелизація
Так, виявляється, читати з файлу одночасно з декількох місць — значно повільніше, ніж робити це послідовно. А значить, щоб обробити файл паралельно, мусимо побудувати складніший скрипт з пулом потоків та розподілом роботи.
Скрипт буде читати з файлу блок за блоком (тут як раз стає до нагоди те, що я вже зробив поблокову обробку) та віддавати їх потокам, а потім збирати результати. Типова архітектура fan-out
. Зробив версії з ракторами та з процесами; різниця в тому, що процеси спілкуються через “труби” IO.pipe
, а у ракторів є своя семантика обміну повідомленнями, яка дуже нагадує мені Golang, бо обмін є синхронним (тобто як відправник, так і отримувач блокують до готовності протилежної сторони.) Код тут.
Наступне відкриття: рівночасні програми на Apple Silicon поводяться неочевидно, тобто, не залучають всі ресурси в системі. Так, мені не вдалося використати 100% ядер; з наявних на моєму MacBook Air 4 “швидких” та 4 “економних” ядер, два швидких залишались вільними, незалежно від кількості ракторів. Макбуки надто розумні — про це можна окремий пост. Навіть так, паралельне рішення працювало у 2-3 рази швидше за найкраще послідовне — нарешті, помітне покращення, хоч і далеке від ідеального.
Щоб уникнути еплівських нюансів, переніс тестовий скрипт на Fly.io. Про це теж треба окремий пост, але головне, що на Fly.io паралельна версія з fork
реально дала восьмикратний приріст, тобто за кількістю ядер. А з іншого боку, розвʼязки з ракторами просто скидають SEGFAULT
- чому я не дуже здивований, бо вони “експериментальні” - хоча могло б бути й краще.
Якщо підсумувати, то так, паралелізувати розвʼязки на Ruby можна, але робити це на процесах (тобто з fork
) та добре продумати обіг даних. Або взяти готові рішення на кшталт Sidekiq та не вигадувати велосипеда.