Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni
🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!31.03.2023
Як я зробив css_parser у 3 рази швидше
Є така хороша бібліотека для Ruby - css_parser. З 88 мільйонами завантажень, це вірний вибір для розбору CSS з середовища Ruby. Зазвичай вона працює добре, але ми виявили, що не для будь-яких документів. На деяких вхідних документах швидкодія падала безнадійно. Ну як — безнадійно? Насправді надія є, можна взяти та зʼясувати, що не так — адже код відкритий.
Так я і зробив; головні покращення можна подивитись в цьому коміті. Сьогодні на них натрапив, та хочу нарешті повернути ці зміни в головну версію бібліотеки. Але поки розкажу, що там помінялось та як я це знайшов.
Власне, головна проблема оригінального коду була в використанні регулярних виразів. В більшості випадків вони працювали нормально; але якщо тільки в документі присутня велика кількість пробілів, як інколи трапляється, то маємо вищезгадане уповільнення — замість частки секунди розбір тривав хвилини.
Але регулярка не робила нічого магічного; вона розділяла вираз CSS на властивості — крапкою з комою, а потім на імʼя та значення — двокрапкою. Це досить легко зробити за допомогою простіших методів String#split
та String#index
.
Далі за допомогою профайлера RubyProf та його режиму відстеження виділення памʼяті я переробив код циклу так, щоб уникнути виділення памʼяті повністю. (Щоб побачити, що проблема в регулярці, теж був потрібен RubyProf, але в більш звичному режимі відстеження часу виконання.)
Ці зміни не тільки покращили швидкість розбору в 3.5 рази та використання памʼяті в 3 рази, але й уникнули тих випадкових проблем з регулярками, з яких все починалось. Як бачите, проблема була не в тому, що Ruby - повільна мова, а в тому, що підхід обраний неоптимальний. Регулярні вирази - потужний інструмент, але ж потужність приходить з ціною; для простих потреб треба брати прості, явні операції на рядках.