דיון בנושא הביצועים בJS
-
המשך להקדמה:
כל נושא הביצועים בJS ברמות העדינות שנדבר פה הם לא רלוונטיים לדפדפן אלא לסביבת שרת (בה כל עיכוב CPU הוא יקר). מכיון שסביבות צד שרת כמעט ואין חוץ מnode אז הדגש שלנו יהיה על V8 שזה המנוע של כרום ונוד.
המקורות שלי מגוונים: מאמרים, פורומים, וגם טסטים אישיים. קצת קשה למצוא כלי בדיקה קל ומדוייק. אני משתמש בעיקר בו http://jsbench.github.io/.טוב נתחיל עם מערכים ואובייקטים.
א. מערך באופן כללי מהיר מאובייקט, למשל:
- אתחול זול (זיכרון) ומהיר יותר ([] VS {}).
- גישה לפי אינדקס מהירה יותר במערך, מאשר גישה לאיבר של אובייקט (arr[1] VS obj.key או obj[key]).
- בחיפוש לפי ערך, במערך יש indexOf שמהר בהרבה מלולאה.
- דחיפת איברים חדשים מהירה יותר במערך (טסט).
ב. פרקטיקות לשימוש נכון במערכים:
- אם אפשר לאתחל מייד עם ערכים, מהר יותר מאשר הכנסה בהמשך.
- כשידוע האורך מראש, יש לאתחל את המערך עם Array(length) (טסט).
- push איטי מגישה ישירות לאינדקס הבא (טסט, דיון בסטאק).
- להימנע מקריאת תאים ריקים (empty). לכן גם לא כדאי למחוק תאים עם delete אם זה לא הכרחי, null יותר טוב (טסט).
- חיפוש:
- לבדוק אם קיים,
arr.includes
. באם לא נתמך (ES5) אזarr.indexOf(x) > -1
. - למצוא לפי קריטריון, find. אם הקיריטריון הוא ערך כל שהוא, כדאי לבנות מילון מאובייקט שמפתחותיו הם הערכים.
- מיקום בהינתן הערך עצמו (במקרה של אובייקט אז המופע), indexOf. בשביל ספירת הופעות במערך, לולאה של indexOf מהירה מfilter ואז length.
- לבדוק אם קיים,
- לולאת for עובדת כמו טיל במערך (אבל יותר לאט מindexOf!), יש גם לשים את המאפיין length במשתנה (קשינג) ולא לפנות אליו בכל איטרציה. forEach נח יותר ובריא לקריאות - אבל במקומות קריטיים השתמשו תמיד בfor.
- מנועי JS מבצעים אופטימיזציה למערכים בעלי אותו סוג (ניחוש הסוג מבוצע ע"י האתחול או ההשמה הראשונה). כלומר אם יש לכם מערך שכולו מספרים, עדיף לא לשים שם אובייקט. אם מדובר במספרים שלמים ישנה אופטימיזציה גדולה אף יותר, ועדיך לא "לשבור" את זה עם איבר עם מספר בעל שבר.
-
@zvizvi אמר בדיון בנושא הביצועים בJS:
[1, 2].includes(1)
מהיר יותר מ
[1, 2].indexOf(1) > 0
זה נכון אבל שים לב שזה שתי פעולות שונות.
למעשה הindexOf מהיר באותה מידה, רק שאתה מוסיף פעולה של בדיקת התוצאה.
המסקנא הנכונה היא שלבדוק אם איבר קיים, לא חכם להשתמש בindexOf. נכון, אוסיף זאת בע"ה, תודה! -
@dovid אמר בתכנות יעיל וביצועים, בJS (בפרט):
indexOf מהיר
עם זאת, חיפוש ע"י גישה למאפיין, מהיר יותר.
[1,2,3].forEach (key => isUpThree[key]: true) isUpThree[2] vs [1,2,3].indexOf(2)
@אהרן אמר בדיון בנושא הביצועים בJS:
@dovid אמר בתכנות יעיל וביצועים, בJS (בפרט):
indexOf מהיר
עם זאת, חיפוש ע"י גישה למאפיין, מהיר יותר.
[1,2,3].forEach (key => isUpThree[key]: true) isUpThree[2] vs [1,2,3].indexOf(2)
גם אתה ציינת עובדה נכונה, אבל השוואה של שני דברים שונים:
בין איתור ערך לגישה ע"י מפתח.
ברור שגישה לאינדקס של אובייקט מנצחת איתור ערך של מערך. -
@אהרן אמר בדיון בנושא הביצועים בJS:
@dovid אמר בתכנות יעיל וביצועים, בJS (בפרט):
indexOf מהיר
עם זאת, חיפוש ע"י גישה למאפיין, מהיר יותר.
[1,2,3].forEach (key => isUpThree[key]: true) isUpThree[2] vs [1,2,3].indexOf(2)
גם אתה ציינת עובדה נכונה, אבל השוואה של שני דברים שונים:
בין איתור ערך לגישה ע"י מפתח.
ברור שגישה לאינדקס של אובייקט מנצחת איתור ערך של מערך.@dovid אמר בדיון בנושא הביצועים בJS:
@אהרן אמר בדיון בנושא הביצועים בJS:
@dovid אמר בתכנות יעיל וביצועים, בJS (בפרט):
indexOf מהיר
עם זאת, חיפוש ע"י גישה למאפיין, מהיר יותר.
[1,2,3].forEach (key => isUpThree[key]: true) isUpThree[2] vs [1,2,3].indexOf(2)
גם אתה ציינת עובדה נכונה, אבל השוואה של שני דברים שונים:
בין איתור ערך לגישה ע"י מפתח.
ברור שגישה לאינדקס של אובייקט מנצחת איתור ערך של מערך.אכן, אתקן א"ע
זה שווה ללבנות אינדקס ל-DB, או לשימוש במצביע.תחשוב על מערך מליון, משתלם מאוד לבנות לו אוביקט כזה.
בפרט אם רוצים להשתמש במיקום בשביל לבדוק רצף, נניח, אז שומרים באוביקט כך
obj['eliezer'] = {8: true, 549: true, 155895: true}
(למה הקוד מתחרבש??)
אז בודקיםObject.keys(ovj['eliezer']).forEach( (startPos) => { if(obj['eat'].startPos+1) {} }
-
אתם צודקים.
יש שלושה אפשרויות באיתור ערך באוסף:- לדעת אם ישנו (includes)
- לקבל אותו לפי קריטריון (find אבל עדיך לבנות מילון)
- לדעת את מיקומו - indexOf.
@אהרן ספציפית את הדוגמה שלך לא הבנתי אבל אני מסכים שלבנות מילון תמיד יותר טוב (חוץ מהזיכרון שב"כ זה שולי).
-
@dovid אמר בתכנות יעיל וביצועים, בJS (בפרט):
אני משתמש בעיקר בו http://jsbench.github.io/.
איך עובדים עם האתר הזה?
איפה שמים את הקוד המקדים?תסתכל ע"ז
http://jsbench.github.io/#13fd0426ba617c8d0e58d87d3f23675b -
אתם צודקים.
יש שלושה אפשרויות באיתור ערך באוסף:- לדעת אם ישנו (includes)
- לקבל אותו לפי קריטריון (find אבל עדיך לבנות מילון)
- לדעת את מיקומו - indexOf.
@אהרן ספציפית את הדוגמה שלך לא הבנתי אבל אני מסכים שלבנות מילון תמיד יותר טוב (חוץ מהזיכרון שב"כ זה שולי).
-
@dovid אמר בתכנות יעיל וביצועים, בJS (בפרט):
אני משתמש בעיקר בו http://jsbench.github.io/.
איך עובדים עם האתר הזה?
איפה שמים את הקוד המקדים?תסתכל ע"ז
http://jsbench.github.io/#13fd0426ba617c8d0e58d87d3f23675b@אהרן אמר בדיון בנושא הביצועים בJS:
@dovid אמר בתכנות יעיל וביצועים, בJS (בפרט):
אני משתמש בעיקר בו http://jsbench.github.io/.
איך עובדים עם האתר הזה?
איפה שמים את הקוד המקדים?למטה בצד שמאל Setup.
תסתכל ע"ז
http://jsbench.github.io/#13fd0426ba617c8d0e58d87d3f23675bאנחנו עוד בArray עוד לא בString.
-
@zvizvi אמר בדיון בנושא הביצועים בJS:
[1, 2].includes(1)
מהיר יותר מ
[1, 2].indexOf(1) > 0
זה נכון אבל שים לב שזה שתי פעולות שונות.
למעשה הindexOf מהיר באותה מידה, רק שאתה מוסיף פעולה של בדיקת התוצאה.
המסקנא הנכונה היא שלבדוק אם איבר קיים, לא חכם להשתמש בindexOf. נכון, אוסיף זאת בע"ה, תודה!@dovid אמר בדיון בנושא הביצועים בJS:
@zvizvi אמר בדיון בנושא הביצועים בJS:
[1, 2].includes(1)
מהיר יותר מ
[1, 2].indexOf(1) > 0
זה נכון אבל שים לב שזה שתי פעולות שונות.
למעשה הindexOf מהיר באותה מידה, רק שאתה מוסיף פעולה של בדיקת התוצאה.
המסקנא הנכונה היא שלבדוק אם איבר קיים, לא חכם להשתמש בindexOf. נכון, אוסיף זאת בע"ה, תודה!הסיבה שהבאתי את זה, כי עד לאחרונה לא היתה האפשרות לבדוק ב
includes
וכולם השתמשו בindexOf >= 0
כאשר הוסיפו את includes חשבתי לתומי שזה סה"כ קיצור דרך ל indexOf >= 0 ואין הבדל בביצועים. לכן בדקתי וזה לא נכון.ועוד משהו includes טיפ-טיפה מהיר יותר גם אם לא תבדוק את התוצאה, עצם זה שהוא מחפש גם את המיקום, כנראה, מוסיף משקל,
בנוסף includes יכול לעצור ברגע שהוא מצא התאמה, indexOf לא. -
-
@dovid אמר בדיון בנושא הביצועים בJS:
@zvizvi אמר בדיון בנושא הביצועים בJS:
[1, 2].includes(1)
מהיר יותר מ
[1, 2].indexOf(1) > 0
זה נכון אבל שים לב שזה שתי פעולות שונות.
למעשה הindexOf מהיר באותה מידה, רק שאתה מוסיף פעולה של בדיקת התוצאה.
המסקנא הנכונה היא שלבדוק אם איבר קיים, לא חכם להשתמש בindexOf. נכון, אוסיף זאת בע"ה, תודה!הסיבה שהבאתי את זה, כי עד לאחרונה לא היתה האפשרות לבדוק ב
includes
וכולם השתמשו בindexOf >= 0
כאשר הוסיפו את includes חשבתי לתומי שזה סה"כ קיצור דרך ל indexOf >= 0 ואין הבדל בביצועים. לכן בדקתי וזה לא נכון.ועוד משהו includes טיפ-טיפה מהיר יותר גם אם לא תבדוק את התוצאה, עצם זה שהוא מחפש גם את המיקום, כנראה, מוסיף משקל,
בנוסף includes יכול לעצור ברגע שהוא מצא התאמה, indexOf לא.@zvizvi אמר בדיון בנושא הביצועים בJS:
ועוד משהו includes טיפ-טיפה מהיר יותר גם אם לא תבדוק את התוצאה, עצם זה שהוא מחפש גם את המיקום, כנראה, מוסיף משקל,
מבדיקה שלי זה לא הצלחתי למצוא הבדל של ממש, ראה http://jsbench.github.io/#82277a913ae25629ce943346c7ce8183
אכן ממה שאנשים אומרים https://stackoverflow.com/q/47659972/1271037 נראה שבגירסאות כרום האחרונות זה מהר יותר.בנוסף includes יכול לעצור ברגע שהוא מצא התאמה, indexOf לא.
למה? שניהם עוצרים ברגע שהם מוצאים.
-
-
@dovid אמר בדיון בנושא הביצועים בJS:
@zvizvi אמר בדיון בנושא הביצועים בJS:
https://coderwall.com/p/_ggh2w/the-array-native-every-filter-map-some-foreach-methods
מה יש במובאה זו?
השוואה בין כל סוגי האיטרציות על מערכים
-
@zvizvi אמר בדיון בנושא הביצועים בJS:
@dovid אמר בדיון בנושא הביצועים בJS:
@zvizvi אמר בדיון בנושא הביצועים בJS:
https://coderwall.com/p/_ggh2w/the-array-native-every-filter-map-some-foreach-methods
מה יש במובאה זו?
השוואה בין כל סוגי האיטרציות על מערכים
אבל לא במובן של ביצועים.
פה יש https://github.com/dg92/Performance-Analysis-JS -
@zvizvi אמר בדיון בנושא הביצועים בJS:
ועוד משהו includes טיפ-טיפה מהיר יותר גם אם לא תבדוק את התוצאה, עצם זה שהוא מחפש גם את המיקום, כנראה, מוסיף משקל,
מבדיקה שלי זה לא הצלחתי למצוא הבדל של ממש, ראה http://jsbench.github.io/#82277a913ae25629ce943346c7ce8183
אכן ממה שאנשים אומרים https://stackoverflow.com/q/47659972/1271037 נראה שבגירסאות כרום האחרונות זה מהר יותר.בנוסף includes יכול לעצור ברגע שהוא מצא התאמה, indexOf לא.
למה? שניהם עוצרים ברגע שהם מוצאים.
@dovid אמר בדיון בנושא הביצועים בJS:
ראה http://jsbench.github.io/#82277a913ae25629ce943346c7ce8183
איפה רשום מה מכיל ARR?
-
@אהרן אמר בדיון בנושא הביצועים בJS:
@dovid אמר בתכנות יעיל וביצועים, בJS (בפרט):
אני משתמש בעיקר בו http://jsbench.github.io/.
איך עובדים עם האתר הזה?
איפה שמים את הקוד המקדים?למטה בצד שמאל Setup.
תסתכל ע"ז
http://jsbench.github.io/#13fd0426ba617c8d0e58d87d3f23675bאנחנו עוד בArray עוד לא בString.
@dovid אמר בדיון בנושא הביצועים בJS:
@אהרן אמר בדיון בנושא הביצועים בJS:
@dovid אמר בתכנות יעיל וביצועים, בJS (בפרט):
אני משתמש בעיקר בו http://jsbench.github.io/.
איך עובדים עם האתר הזה?
איפה שמים את הקוד המקדים?למטה בצד שמאל Setup.
-
@dovid אמר בדיון בנושא הביצועים בJS:
@אהרן אמר בדיון בנושא הביצועים בJS:
@dovid אמר בתכנות יעיל וביצועים, בJS (בפרט):
אני משתמש בעיקר בו http://jsbench.github.io/.
איך עובדים עם האתר הזה?
איפה שמים את הקוד המקדים?למטה בצד שמאל Setup.
-
@אהרן אמר בדיון בנושא הביצועים בJS:
בנוגע ל-PUSH
זה מראה להיפך.תודה רבה רבה! עדכנתי את הפוסט.
היייתה לי טעות פשוטה מאוד בטסט! שמתי את ההכרזה הSetup, ממילא אחרי הדגימה הראשונה המערך הוא כבר בן 1000 איברים, אז ברור שהאינדקס מהיר מהוספה...
עדכנתי כעת את הטסט. הטסט שהבאת מצויין אבל תראה שפעם האינדקס ינצח ופעם הpush, התוצאות שמה נכונות לגירסאות ישנות של כרום.