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

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

31.03.2023

Як я зробив css_parser у 3 рази швидше

Є така хороша бібліотека для Ruby - css_parser. З 88 мільйонами завантажень, це вірний вибір для розбору CSS з середовища Ruby. Зазвичай вона працює добре, але ми виявили, що не для будь-яких документів. На деяких вхідних документах швидкодія падала безнадійно. Ну як — безнадійно? Насправді надія є, можна взяти та зʼясувати, що не так — адже код відкритий.

Так я і зробив; головні покращення можна подивитись в цьому коміті. Сьогодні на них натрапив, та хочу нарешті повернути ці зміни в головну версію бібліотеки. Але поки розкажу, що там помінялось та як я це знайшов.

Власне, головна проблема оригінального коду була в використанні регулярних виразів. В більшості випадків вони працювали нормально; але якщо тільки в документі присутня велика кількість пробілів, як інколи трапляється, то маємо вищезгадане уповільнення — замість частки секунди розбір тривав хвилини.

Але регулярка не робила нічого магічного; вона розділяла вираз CSS на властивості — крапкою з комою, а потім на імʼя та значення — двокрапкою. Це досить легко зробити за допомогою простіших методів String#split та String#index.

Далі за допомогою профайлера RubyProf та його режиму відстеження виділення памʼяті я переробив код циклу так, щоб уникнути виділення памʼяті повністю. (Щоб побачити, що проблема в регулярці, теж був потрібен RubyProf, але в більш звичному режимі відстеження часу виконання.)

Ці зміни не тільки покращили швидкість розбору в 3.5 рази та використання памʼяті в 3 рази, але й уникнули тих випадкових проблем з регулярками, з яких все починалось. Як бачите, проблема була не в тому, що Ruby - повільна мова, а в тому, що підхід обраний неоптимальний. Регулярні вирази - потужний інструмент, але ж потужність приходить з ціною; для простих потреб треба брати прості, явні операції на рядках.