האמת היא שאני משתמש בכלי הזה כבר זמן רב מאוד והייתי בטוח שמדובר ב-common knowledge עבור מפתחי פרונט אנד. אבל בזמן האחרון יצא לי לדבר עם כמה אנשים שאני ממש מחזיק מהם והם לא הכירו – אז חשבתי לחלוק את הידע הזה. עם מי שמכיר – הסליחה 🙂
כאשר אנו מפתחים ב-JavaScript, עניין הביצועים יכול להיות לפעמים קריטי – במיוחד אם האפליקציה/אתר שלנו אמורים לעבוד עם דפדפנים ישנים יותר או דפדפני מובייל. למי שלא יודע, לא כל דפדפן מריץ JavaScript באותה יעילות. יש דפדפנים שמריצים את הסקריפטים במהירות מסחררת ויש כאלו שאפילו סקריפטים פשוטים יחסית יתקעו למשך הרבה זמן. ההבדלים בין הדפדפנים והפלטפורמות השונות יכולים להיות דרמטיים. כשאני אומר דרמטיים אני מתכוון להבדלים של שניות רבות. וזה מאוד מאוד לא נעים לטפל בבעיות ביצועים בדפדפן/פלטפורמה מסוימת.
הדרך הטובה ביותר לטפל בבעית ביצועים היא לטפל בה לפני שהיא מתרחשת – כלומר לכתוב קוד JavaScript יעיל יותר עוד בשלב הכתיבה ולא בשלב הדיבוג וכמובן לנטר את מהירות האפליקציה שלכם כל הזמן. בנוגע לכתיבת קוד יעיל – בזמנו כתבתי מאמר על אירועים יעילים יותר ב-JavaScript וכן גם על סלקטורים יעילים יותר. בקטגוריה של jQuery למתקדמים יש לא מעט טיפים בנושא וכמובן שיש גם במקורות אחרים ברשת מידע עצום ורב שלא נמצא באינטרנט ישראל. הבנה גם של ECMAScript ואיך היא עובדת יכול לסייע פה.
ניטור מהירות האפליקציה יכול להתבצע באמצעות דיבוג הסקריפט עצמו עם הפרופיילר או בלעדיו. גישה הוליסטית יותר היא להשתמש בפריימוורק לבדיקה כמו JSLitmus למשל או שימוש בכלים מתקדמים יותר במידה והאפליקציה שלכם מבוססת JavaScript.
אחד מהכלים השימושיים יותר שמאפשר לי למדוד בדיוק מה יעיל יותר ומה לא על דפדפנים שונים ולזהות נקודות תורפה הוא JSperf. לא מדובר בכלי שניתן להטמיע אלא בשירות פשוט וקל המאפשר לי להשוות ביצועים בין סניפטים שונים של JavaScript – עם ספריות/פריימוורקים או בלי.
למשל, נניח ואני כותב לולאה באחד הסקריפטים שלי ואני רוצה לדעת מה יעיל יותר – האם לולאת while? לולאת for? שימוש ב do while ? יכול להיות שההבדל נראה לכם זניח, אבל גם בכרום דסקטופ, שנכון לכתיבת שורות אלו הוא דפדפן שמריץ JavaScript במהירות הגבוהה ביותר שיש, ההבדל הוא כ-30 אחוז בין השיטה המהירה ביותר לשיטה האיטית ביותר. וזה במקרה של כרום ובמקרה של מערך פשוט. ההבדלים יכולים להיות אדירים אם מדובר במערך ארוך ובדפדפן של אנדרואיד שמריץ JavaScript כמו אפסנאי שצריך לנפק נעליים קלות.
אז איך בודקים? בדיוק בשביל זה יש את JSperf שמאפשר לנו לכתוב בדיקות ולהריץ אותן על הדפדפנים שלנו, להסתכל על בדיקות של אחרים ולראות מה היו התוצאות על הדפדפנים שלהם (ולהריץ אותם גם על שלנו) ולשנות את הבדיקות של אנשים אחרים כמובן. כלי אדיר!
יצירת בדיקה
איך משתמשים? מאוד פשוט. נכנסים ל-JSperf וממלאים את השדות הנדרשים מבדיקה חדשה – המינימום זה שם הבדיקה וה-URL שבו הבדיקה תשמר (jsperf.com/whatever). אחרי זה, אנו נדרשים להגדיר מספר דברים:
ראשית – ה-HTML שעליו תרוץ הבדיקה. כיוון שעדיין ברוב המקרים JavaScript עובד בצמוד ל-HTML, יש לנו לפעמים צורך לכתוב HTML שהסקריפט שנבדוק ירוץ עליו. זה יכול להיות HTML ארוך או קצר או אפילו איזה div אחד פשוט ומסכן שיש לו class למשל.
שנית – setup ו-teardown. לא מדובר בחובה. setup הוא סקריפט שרץ לפני כל סקריפט שנבדק ו teardown הוא סקריפט שרץ אחרי כל סקריפט שנבדק. מדובר במתודלוגיה ותיקה של בדיקות שמאפשרת לנו לנקות אחרינו או לבנות את סביבת הבדיקות. אם אתם לא יודעים מה זה ולא הבנתם את ההסבר שלי – פשוט תתעלמו.
שלישית – וזה הכי חשוב – ה-test cases! כל מה שעלינו לעשות זה לכתוב בכל test case את הסקריפט שאנו רוצים לבדוק. אם, למשל, אנו רוצים להשוות בין כמה סוגי לולאות, בכל testcase נכתוב סוג אחר של לולאה.
הכי טוב זה להסביר עם דוגמה פשוטה! הכנסו לבדיקה הזו המשווה בין סוגי לולאות שונות ותבדקו איך היא כתובה. זה כל כך פשוט שזה מצחיק.
בדיקת התוצאות
אחרי שיצרנו את הבדיקה, אנחנו יכולים ללחוץ על הכפתור Run test (צד ימין למטה). הדפדפן מתחיל להריץ את הסקריפטים שכתבנו. מספר הפעמים שבהם אפשר להריץ את הסקריפט בשניה אחת נמדד מספר פעמים והממוצע מוצג בפניכם לדפדפן שלכם. כך למשל, אפשר לראות שבכרום גרסה 30, לולאה מסוג for שיש בה cache שנראית כך:
for (var i = 0, len = arr.length; i < len; i++) {
someFn(i);
}
תרוץ 783,000 פעמים בשניה בעוד לולאה מסוג do while שרצה לאחור:
var i = arr.length - 1;
do
{
someFn(i);
}
while (i--);
תרוץ רק 544 אלף פעמים בשניה. זה הבדל דרמטי של קצת יותר מ-30 אחוזים! ליד כל תוצאה גם מוצגת סטיית התקן.
אם אריץ את הבדיקה בפיירפוקס 26 אפשר לראות שלולאת ה-for היא אכן מהירה יותר ורצה 405,000 פעמים בשניה. מדוע יש הבדלים דרמטיים כל כך? למה כרום מריץ את הלולאה שבע מאות אלף פעם בעוד שפיירפוקס מריץ ארבע מאות אלף פעם? הסיבה היא שהמנוע של כרום יעיל הרבה יותר.
אם נסתכל על הבדיקה האיטית יותר נראה שבפיירפוקס לולאת ה-while הקלאסית היא איטית יותר אבל לא בהבדל דרמטי מאוד היא רצה 377,000 פעם בשניה. למה כאן יש שוני? כי לא רק שמנועי ה-JavaScript מהירים יותר או פחות מדפדפן לדפדפן אלא גם הם שונים ומריצים דברים מסוימים מהיר או פחות. יש גם שוני בין מחשבים שונים ומערכות הפעלה שונות. איך jsPerf מתמודד עם הקושי הזה? הוא אוגר את התוצאות של כל מי שבודק ומציג את הכל ביחד. כך שבבדיקות פופולריות אפשר לראות ממוצע של הרבה מאוד משתמשים.
אגב, מה בנוגע למצטיין הכיתה? אינטרנט אקספלורר 11 החדיש, הנוצץ והמשוכלל, פאר רדמונד ויקיר ביל גייטס? טוב. הוא מריץ היטב דווקא את לולאת ה-for הקלאסית:
for( var i = 0; i < arr.length; i++ ) {
someFn(i);
}
הקצב המסחרר שבה הוא מריץ את הלולאה הזו יעיף אתכם: לא פחות מ 22,000 פעמים בשניה. השוו נא לארבע מאות אלף של פיירפוקס ולשבע מאות האלף של כרום ותבינו למה התפלץ הכחול הוא יקיר המפתחים…
האמת שזה לא מאוד פייר כי יש גם הבדלים בין גרסאות. גרסת 10 של אינטרנט אקספלורר הארור נותנת פייט יותר טוב עם 80,000 הרצות לשניה… 😛 סתם, כי זה מאוד תלוי בקוד, יש סקריפטים שאקספלורר יתמודד איתם הרבה יותר טוב ואפילו יותר טוב מפיירפוקס או כרום.
התוצאות של הרינדור יישמרו ויאספו וניתן לגלול למטה ולראות את התוצאות של כל הדפדפנים של כל המשתמשים שהריצו את הבדיקות השונות. במה לבחור? זה כבר שאלה אחרת. אני בדרך כלל בוחר מה שהכי מהיר באינטרנט אקספלורר מהגרסה הנמוכה ביותר שאני צריך לתמוך בה או אם מדובר במשהו שרץ במובייל – באנדרואיד מהגרסה הנמוכה ביותר שאני צריך לתמוך בה.
עד כאן בנוגע לתוצאות. ברוב המקרים, אם יש לכם התלבטות בין שיטות או מתודות שונות, אפשר תמיד לכתוב method v1 VS method v2 jsperf בגוגל (תחליפו את method v1 ו v2 בשמות של הדברים שאתם רוצים לבדוק כמובן) ולראות בדיקות שאנשים אחרים בדקו. הרבה פעמים מגלים בגרסאות השונות כל מיני שיטות שלא חשבתם עליהן.