Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni

🤖🚫 Контент вільний від AI. Цей пост на 100% написаний людиною, як і все на моєму блозі. Насолоджуйтесь!

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 та не вигадувати велосипеда.