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

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

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

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

שיפור ביצועים בקליק, פרק 1: מערכים

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

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

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

JavaScript code snippet to check if two arrays have at least one common element. The code defines arrays `source` and `target` and implements a function `isIntersect` using `Array.prototype.some` and `includes` to detect intersections.

האם זה נראה לא בסדר? אתם מזהים כאן איזו בעיה פוטנציאלית?

במבט ראשון, הקוד נראה בסדר גמור. הוא בודק אם לפחות אחד מהפריטים במערך source קיים במערך target. הקוד קצר, משתמש ב-API המובנה של JavaScript, ונראה כמו פתרון מצוין לצורך כמו מציאת ה-Intersection 🚀

אבל האם הוא באמת יעיל כמו שהוא נראה?

בדיקת ביצועים: איך Array.includes מתמודד עם מערכים גדולים?

בואו ננסה את הפתרון הזה על מערכים גדולים יותר:

שיפור ביצועים - GIF demonstrating the high performance of the JavaScript code from the previous image. The animation shows a fast execution of the `isIntersect` function, instantly checking for common elements between two arrays when clicking the "Run Script" button in a neon-style UI, with Chrome DevTools open in the "Sources" tab.

הרצנו את הקוד שוב, והוא עדיין עובד במהירות 💪 אין דגלים אדומים, נכון?

השוואת Array.includes ו-Set במערכים גדולים – איך מוביל לשיפור הביצועים?

בואו נעלה הילוך. מה לגבי מערכים עם 60,000 אייטמים?

שיפור ביצועים - GIF demonstrating the performance slowdown of the JavaScript `isIntersect` function when handling large datasets. The animation shows the execution taking noticeably longer as the array sizes increase, highlighting performance limitations. The "Run Script" button is clicked in a neon-style UI, with Chrome DevTools open in the "Sources" tab to observe execution time.

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

אז מה קורה כאן? בואו ננסה לפרק את זה.

הבעיה

מקור הבעיה הוא בפעולות המקוננות:

  • Array.includes מבצע חיפוש ליניארי, כלומר רץ בסיבוכיות של O(n)
  • Array.some עובר דרך המערך source, כלומר בסיבוכיות של O(m)

השילוב הזה מוביל למורכבות כוללת של O(n * m), מה שעשוי להפוך לאסון כשעובדים עם כמות גדולה של נתונים🥵

שיפור ביצועים ב-JavaScript: איך Set ו-Map מייעלים חיפושים?

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

שיפור ביצועים - GIF demonstrating the high performance of the optimized `isIntersect` function in JavaScript, even with large datasets. The animation shows the function executing quickly by leveraging an object-like array as a lookup table, significantly improving efficiency over `.includes()`. The "Run Script" button is clicked in a neon-style UI, with Chrome DevTools open in the "Sources" tab, highlighting the fast execution time.

וואו! גם עם 120K איברים, אנחנו בקושי מרגישים את זמן הריצה 🥳

מה הקסם?

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

שימוש ב-Object:

Optimized JavaScript implementation of the `isIntersect` function for checking common elements between two arrays. This version enhances performance by using a plain object (`obj`) as a lookup table instead of an array, reducing lookup time complexity to O(1) for large datasets. The function efficiently maps `target` elements into `obj` and checks for intersections using the `in` operator.
שיפור ביצועים - GIF demonstrating the high performance of the optimized `isIntersect` function in JavaScript, even with large datasets. The animation shows the function executing quickly by leveraging an object as a lookup table, significantly improving efficiency over `.includes()`. The "Run Script" button is clicked in a neon-style UI, with Chrome DevTools open in the "Sources" tab, highlighting the fast execution time.

ועכשיו, אותו עקרון רק עם Map:

Optimized JavaScript implementation of the `isIntersect` function using a `Map` for efficient lookups. This version converts the `target` array into key-value pairs using `map()`, then initializes a `Map` for O(1) lookups. The function checks for common elements between two arrays using `map.has(item)`, improving performance for large
שיפור ביצועים - GIF demonstrating the high performance of the `isIntersect` function optimized with a `Map` for efficient lookups. The animation shows the function executing quickly, even with large datasets, as `Map` enables O(1) lookups compared to traditional array searches. The "Run Script" button is clicked in a neon-style UI, with Chrome DevTools open in the "Sources" tab, highlighting the fast execution time.

והאופציה הטובה ביותר, חיפוש באמצעות Set:

Optimized JavaScript implementation of the `isIntersect` function using a `Set` for fast lookups. This version converts the `target` array into a `Set`, enabling O(1) lookups when checking for common elements with `set.has(item)`. This approach significantly improves performance compared to array-based searches, especially for large datasets.
שיפור ביצועים - GIF demonstrating the high performance of the `isIntersect` function optimized with a `Set` for efficient lookups. The animation shows the function executing rapidly, even with large datasets, as `Set` provides O(1) lookup time compared to traditional array searches. The "Run Script" button is clicked in a neon-style UI, with Chrome DevTools open in the "Sources" tab, highlighting the fast execution time.

השגת סיבוכיות של O(1) לחיפושים היא היתרון המרכזי של האפשרויות הללו, ולכן הן עדיפות על הגישה המקורית.

שיפור ביצועים: Array.includes מול Set מול Map – מי המהיר ביותר?

בהינתן ש-n ו-m הם כמות האייטמים במערכים source ו-target, ו-k הוא הטווח של הערכים במערך target, אז:

הגישה המקורית:

  • Time Complexity: O(n * m)
  • Space Complexity: O(n + m)

הגישה החלופית:

  • Time Complexity: O(n + m)
  • Space Complexity: O(k)

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

סיכום

מעבר מ-Array.includes למבנה נתונים מתאים יותר כמו Set או מערך מאונדקס יכול לשפר באופן דרמטי את הביצועים – במיוחד כשעובדים עם כמויות נתונים גדולות.

ובאופן כללי – גם שורת קוד תמימה, מסתירה לפעמים בעיות ביצועים חמורות כשנעבוד עם Scale גדול יותר. ככל שנעמיק ב״מאחורי הקלעים״ של JavaScript, נוכל לכתוב קוד שהוא לא רק נכון, אלא גם יעיל במיוחד.


רוצה עוד טיפים לאופטימיזציה של ביצועי 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

Dramatic illustration of a wrestling match where "Set" triumphs over "Array." A neon-lit figure labeled "SET" stands victoriously in the ring, while a defeated "ARRAY" lies on the ground wrapped in glowing wires. The cheering crowd holds signs in support of "Set" and "Array," visually symbolizing the performance superiority of JavaScript's `Set` over arrays for fast lookups.

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

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

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

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

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

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

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