מחקר מקרה: איך צוות Gmail Wear OS שיפר את הפעלת האפליקציה ב-50%

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

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

איך מצלמים מעקב ובודקים את הפעלת האפליקציה

כדי להתחיל בניתוח, מצלמים עקבות שכוללות את הפעלת האפליקציה לבדיקה מדוקדקת יותר ב-Perfetto או ב-Android Studio. במקרה השימוש הזה, אנחנו משתמשים ב-Perfetto כי הוא מראה לכם מה קורה במערכת המכשיר, מעבר לאפליקציה שלכם. כשמעלים את הנתונים ל-Perfetto, הם נראים כך:

איור 1. תצוגה ראשונית של העקבות ב-Perfetto.

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

שורה של הפעלות אפליקציות ב-Android עם אפשרות להצמיד את האפליקציה המודגשת.
איור 2. כדי שיהיה קל יותר לנתח את הנתונים, אפשר להצמיד את המדד המותאם אישית 'הפעלות של אפליקציות Android' לראש לוח הבקרה.

הערה: המדד Android App Startups מייצג את הזמן עד להצגה הראשונית, גם אם אתם משתמשים ב-reportFullyDrawn(). כדי לזהות את הזמן עד להצגה מלאה, מחפשים את reportFullyDrawn() בתיבת החיפוש של Perfetto.

בדיקת ה-thread הראשי

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

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

הצמדת השורות 'הפעלות של אפליקציות ל-Android' ו'thread ראשי'.
איור 3. כדי להקל על הניתוח, מצמידים את השורות של ה-main thread מתחת למדד המותאם אישית Android App Startups.

משך הזמן במצב הפעלה והתחרות על משאבי ה-CPU

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

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

השרשור הראשי מודגש עם הזמן הכולל במצבים שונים בחלונית Thread States (מצבי השרשור).
איור 4. כדי לקבל מושג ראשוני לגבי העומס על המעבד, אפשר להעריך את הזמן היחסי במצבי Runnable עד Running.

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

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

משך הזמן בOpenDexFilesFromOat*

עכשיו בודקים את הזמן שחלף ב-OpenDexFilesFromOat*. בנתוני המעקב, זה קורה באותו הזמן כמו הפלח bindApplication. הפלח הזה מייצג את הזמן שלוקח לקרוא את קובצי ה-DEX של האפליקציה.

עסקאות חסומות של כרטיס מועדון

לאחר מכן בודקים את העסקאות של הכרטיס. עסקאות Binder מייצגות שיחות בין הלקוח לשרת: במקרה הזה, האפליקציה (הלקוח) קוראת למערכת Android (השרת) עם binder transaction, והשרת מגיב עם binder reply. חשוב לוודא שהאפליקציה לא מבצעת עסקאות מיותרות של Binder במהלך ההפעלה, כי הן מגדילות את הסיכון להתנגשות בין תהליכים במעבד. אם אפשר, כדאי לדחות את העבודה שכוללת קריאות ל-binder לתקופה שאחרי הפעלת האפליקציה. אם אתם צריכים לבצע עסקאות של Binder, ודאו שהן לא נמשכות יותר מקצב הרענון של Vsync במכשיר.

העסקה הראשונה של ה-binder, שמתרחשת בדרך כלל באותו זמן כמו ActivityThreadMain slice, נראית ארוכה למדי במקרה הזה. כדי לקבל מידע נוסף על מה שקורה, פועלים לפי השלבים הבאים:

  1. כדי לראות את התשובה המשויכת של ה-binder ולקבל מידע נוסף על סדר העדיפויות של העסקה של ה-binder, לוחצים על פרוסת העסקה של ה-binder שרוצים לבדוק.
  2. כדי לראות את התשובה של הכרטיסייה, עוברים לחלונית Current Selection ולוחצים על binder reply בקטע Following threads. בשדה Thread (שרשור) מופיע גם השרשור שבו מתקבלת התשובה של ה-binder, אם רוצים לנווט אליו באופן ידני. התשובה תופיע בתהליך אחר. מופיעה שורה שמקשרת בין הטרנזקציה של הכריכה לבין התשובה.

    קו מחבר בין השיחה והתשובה.
    איור 5. מזהים את עסקאות ה-binder שמתרחשות במהלך הפעלת האפליקציה ומעריכים אם אפשר לדחות אותן.
  3. כדי לראות איך שרת המערכת מטפל בעסקת ה-Binder הזו, מצמידים את השרשורים Cpu 0 ו-Cpu 1 לחלק העליון של המסך.

  4. מחפשים את התהליכים במערכת שמטפלים בתשובת ה-Binder. לשם כך, מחפשים את הפרוסות שכוללות את שם השרשור של תשובת ה-Binder. במקרה הזה, 'Binder:687_11 [2542]'. לוחצים על התהליכים הרלוונטיים במערכת כדי לקבל מידע נוסף על העסקה של הכריכה.

כדאי לעיין בתהליך המערכת הזה שמשויך לעסקת ה-Binder שמעניינת אותך ומתרחשת במעבד 0:

תהליך מערכת עם מצב סיום 'ניתן להפעלה (נמנע)'.
איור 6. תהליך המערכת נמצא במצב Runnable (Preempted), שמציין שהוא מתעכב.

במצב הסיום כתוב Runnable (Preempted), כלומר התהליך מתעכב כי המעבד עושה משהו אחר. כדי לגלות מה גורם להפסקת הפעולה, מרחיבים את השורות Ftrace Events. בכרטיסייה Ftrace Events שמופיעה, גוללים ומחפשים אירועים שקשורים לשרשור של Binder שמעניין אתכם, למשל Binder:687_11 [2542]. בסביבות הזמן שבו תהליך המערכת נדחה, התרחשו שני אירועים של שרת המערכת שכוללים את הארגומנט decon, כלומר הם קשורים לבקר התצוגה. זה נשמע הגיוני כי בקר התצוגה מציב את הפריימים על המסך – משימה חשובה! אז לא צריך לשנות את האירועים.

אירועי FTrace שמשויכים לטרנזקציית ה-binder הרלוונטית מודגשים.
איור 7. האירועים של FTrace מציינים שהעסקה של binder מתעכבת בגלל אירועים של בקר התצוגה.

פעילות JIT

כדי לבדוק את הפעילות של קומפילציה בזמן אמת (JIT), מרחיבים את התהליכים ששייכים לאפליקציה, מאתרים את שתי השורות של 'מאגר שרשורים של JIT' ומצמידים אותן לחלק העליון של התצוגה. מכיוון שהאפליקציה הזו נהנית מפרופילים של Baseline במהלך הפעלת האפליקציה, יש מעט מאוד פעילות של JIT עד לציור הפריימים הראשון, שמסומן בסיום הקריאה הראשונה של Choreographer.doFrame. עם זאת, שימו לב לסיבה JIT compiling void להפעלה האיטית, שמצביעה על כך שפעילות המערכת שמתרחשת במהלך נקודת המעקב שמסומנת בתווית Application creation גורמת להרבה פעילות JIT ברקע. כדי לפתור את הבעיה, מוסיפים לפרופיל הבסיסי את האירועים שמתרחשים זמן קצר אחרי שהפריים הראשון מצויר. לשם כך, מרחיבים את אוסף הפרופילים עד שהאפליקציה מוכנה לשימוש. במקרים רבים, אפשר לעשות את זה על ידי הוספת שורה לסוף הבדיקה של מאקרו-מדד הביצועים של איסוף פרופיל הבסיס, שממתינה להופעת ווידג'ט מסוים בממשק המשתמש במסך, מה שמציין שהמסך מאוכלס באופן מלא.

מאגרי פרוטוקולי Thread של Jit עם פרוסת Jit compiling void (הידור Jit) מודגשת.
איור 8. אם אתם רואים הרבה פעילות של JIT, צריך להרחיב את פרופיל הבסיס עד שהאפליקציה תהיה מוכנה לשימוש.

תוצאות

בעקבות הניתוח הזה, צוות Gmail Wear OS ביצע את השיפורים הבאים:

  • הם הבחינו בהתנגשות במהלך הפעלת האפליקציה כשבדקו את פעילות המעבד, ולכן החליפו את אנימציית הספינר ששימשה לציון טעינת האפליקציה בתמונה סטטית אחת. בנוסף, הם האריכו את משך הזמן של מסך הפתיחה כדי לדחות את מצב הניצוץ, המצב השני של המסך שמשמש לציון שהאפליקציה נטענת, כדי לפנות משאבי CPU. השיפור הזה קיצר את זמן האחזור של הפעלת האפליקציה ב-50%.
  • לאחר בדיקת הזמן שחלף ב-OpenDexFilesFromOat* וב-JIT activity, הם הפעילו את R8 כדי לשכתב את Baseline Profiles. השיפור הזה קיצר את זמן האחזור של הפעלת האפליקציה ב-20%.

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

  • הגדרת תהליך מתמשך שיכול לאסוף באופן אוטומטי עקבות ותוצאות. מומלץ להגדיר מעקב אוטומטי באפליקציה באמצעות השוואה לשוק.
  • כדאי להשתמש בבדיקות A/B כדי לבדוק שינויים שלדעתכם ישפרו את המצב, ולדחות אותם אם הם לא משפרים. אפשר למדוד את הביצועים בתרחישים שונים באמצעות ספריית Macrobenchmark.

מידע נוסף זמין במשאבים הבאים: