הרשמה לניוזלטר

הרשמה לניוזלטר

הירשמו לקבלת תוכן איכותי מעולם הפרונט לתיבת הדואר הנכנס, כל חודש.

Ⓒ כל הזכויות שמורות ל- Fed Cast – קהילת מפתחי הפרונט בישראל

שיפור ביצועים בקליק, פרק 3: Regex

ביטויים רגולריים הם כלי עוצמתי לעיבוד טקסט מורכב, אבל ידעתם שהם עלולים להאט את הביצועים של האפליקציה שלכם באופן דרמטי? הפעם נגלה את החולשות של Regex, ונראה איך אפשר להשיג שיפור ביצועים על ידי שימוש באלטרנטיבות מהירות יותר, פשוטות יותר וקריאות יותר.

דמיינו שיש לכם כמות גדולה של פסקאות, כאשר כל אחת מהן מכילה 20,000 תווים. כל פסקה מתחילה ב-foo ומסתיימת ב-bar. אם תרצו לבדוק שכל פסקה עומדת במבנה הזה, כנראה תכתבו משהו כזה:

קוד שמדגים ביטוי רגולרי איטי

במבט ראשון, זה עובד מצוין, אפילו עם 10,000 פסקאות:

סרטון שמדגים ביטוי רגולרי איטי על כמות קטנה

אבל מה קורה כשמגדילים את הכמות? אם, למשל, נגדיל למיליון פסקאות, נגלה שהבדיקה הופכת להיות איטית מאוד:

סרטון שמדגים ביטוי רגולרי איטי על כמות גדולה

איך אפשר לבצע שיפור ביצועים?

האם עלינו להמתין כל כך הרבה זמן עד שהבדיקה תסתיים? ממש לא. אפשר לפתור את זה בקלות עם הקוד הבא:

התוצאה? אפילו אם נריץ את זה על 50 מיליון (!) פסקאות, זה כמעט לא יורגש:

סרטון שמדגים שיפור ביצועים של ביטוי רגולרי

אז למה Regex איטי כאן?

כדי להבין למה ה-Regex הופך לאיטי כל כך, נצטרך להבין מה קורה מאחורי הקלעים. הביטוי הרגולרי שבנינו קודם מפעיל שלוש בדיקות נפרדות:

  1. ^foo – בודק שהטקסט מתחיל ב-foo.
  2. bar$ – בודק שהטקסט מסתיים ב-bar.
  3. .* עם הדגל s – מבטיח שכל תו בין foo ל-bar הוא… ובכן, תו כלשהו 😂 וזה בדיוק החלק הבעייתי!

ה-.* מכריח את מנוע ה-Regex לעבור על כל תו בטקסט, למרות שאנחנו יודעים שאין בזה שום צורך. זה גורם לסיבוכיות של O(n), מה שהופך להיות צוואר הבקבוק מבחינת הביצועים.

מהצד השני, startsWith ו-endsWith לא דורשים מעבר על כל המחרוזת, אלא מבצעים בדיקה ישירה על ההתחלה והסוף בלבד ומספקים לנו יעילות מקסימלית.

האם חייבים לוותר על Regex בשביל שיפור ביצועים?

זה אולי נראה ש-startsWith ו-endsWith הם בסך הכל "sugar syntax" לבדיקה עם Regex, כי אפשר לבצע בדיקה זהה עם הקוד הבא:

דוגמא נוספת לביטוי רגולרי איטי

אבל, למרות שהם נראים זהים, בדיקה פשוטה תגלה שכשמריצים את זה 50 מיליון פעמים, זה איטי כמעט פי 5!

סרטון של דוגמא נוספת לביטוי רגולרי איטי

לכן, המסקנה הבלתי נמנעת היא ש-Regex לא נועד לספק את הביצועים הטובים ביותר, וכשמדובר בכמויות גדולות של נתונים או פעולות, עדיף לשקול אלטרנטיבות כמו זאת שראינו.

וחוץ מזה, בינינו – פונקציות כמו startsWith ו-endsWith עושות את הקוד להרבה יותר קריא ואלגנטי, לא? 😉

דוגמה נוספת: שיפור ביצועים של חיפוש בקצוות הטקסט

בואו נראה עוד דוגמה ליעילות של פונקציות חלופיות: נניח שאנחנו רוצים לבדוק אם foo מופיע איפשהו בתוך 12 התווים הראשונים, ו-bar איפשהו בתוך 12 התווים האחרונים. עם Regex, הקוד ייראה בערך ככה:

דוגמא נוספת לביטוי רגולרי איטי

ושוב, תוצאות לא טובות:

סרטון של דוגמא נוספת לביטוי רגולרי איטי

הפעם, נראה ש-startsWith ו-endsWith לא יכולים לעזור לנו, כי אנחנו לא מחפשים משהו שמופיע בדיוק בתחילת או בסוף המחרוזת. למרות זאת, אנחנו יכולים להשתמש בפונקציות אחרות כדי לשפר את הקריאות ובעיקר – להאיץ את המהירות באופן משמעותי:

דוגמא נוספת של שיפור ביצועים של ביטוי רגולרי

ותראו איך גם כשאנחנו מריצים את זה 100 מיליון פעמים, זה פשוט עובד בצורה חלקה:

סרטון של דוגמא נוספת של שיפור ביצועים של ביטוי רגולרי

היינו יכולים לחשוב ששימוש ב-substring, שיוצר בפועל מערך חדש בכל איטרציה, הוא יקר יותר מאשר Regex, אבל מסתבר שלא – הפעולה הזאת זולה בהרבה ותיתן לנו את אותה בדיקה בדיוק, עם כמעט אפס Overhead בביצוע!

(כמובן, יכולנו לממש את החיפוש בעצמנו באמצעות לולאה שעוברת על המחרוזת עד למיקום 12 במקום להשתמש ב-substring, אבל השיפור המזערי שהיינו מקבלים כאן לא שווה את הקוד המורכב והקריא-פחות שהיינו צריכים לתחזק).

יש עוד דוגמאות?

ברור! הנה כמה דוגמאות נוספות שממחישות איך שימוש בפונקציות מובנות של JavaScript יכול לחסוך זמן ריצה יקר. נסו להריץ אותן ותראו עד כמה ההבדל משמעותי כשזה מגיע לכמויות גדולות:

או למשל:

דוגמא נוספת של שיפור ביצועים של ביטוי רגולרי

סיכום

כמו תמיד, פיצ'רים מובנים בשפה הם עוצמתיים, אבל חשוב להבין איך להשתמש בהם באופן נכון ויעיל. שימוש לא זהיר או בלי הבנה מעמיקה מספיק עלול לגרום לבעיות ביצועים משמעותיות. על ידי הבנה של מה שקורה מאחורי הקלעים, אפשר לכתוב קוד מהיר, נקי ויעיל בהרבה.

Regex הוא כלי חזק – תשתמשו בו בחוכמה! 🚀


רוצה עוד טיפים לאופטימיזציה של ביצועי JavaScript? תעקבו אחרי המאמרים הנוספים בסדרה! 🚀

👈 לקריאת המאמר הקודם בסדרה על שיפור ביצועים בעבודה עם פורמטינג של תאריכים, מטבעות ואחוזים.

מאמרים קשורים

The Hidden Cost of JS Arrays

The Dark Side of JS Formatting

Are you aware of this Regex pitfall

Think One WebWorker is Enough? Think Again

שלום לך 👋 נעים להכיר.

הירשמו לקבלת תוכן איכותי מעולם הפרונט לתיבת הדואר הנכנס, כל חודש.

אנחנו לא שולחים ספאם!

רוצים לקבל מאיתנו עדכונים?

אם עולם הפרונט מעניין אתכם ואתם רוצים לקבל עדכונים ישירות למייל על כל המאמרים הכי מעניינים, המשרות החדשות, הפודקאסטים הכי נשמעים ועוד הרבה תוכן משובח, הירשמו לניוזלטר שלנו והישארו מעודכנים!

הרשמה לניוזלטר

הירשמו לקבלת תוכן איכותי מעולם הפרונט לתיבת הדואר הנכנס, כל חודש.