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

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

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

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

Runnable
עד Running
.ככל שהיחס בין הזמן במצב Runnable
לבין הזמן במצב Running
גבוה יותר, כך גדל הסיכוי שמתרחשת תחרות על משאבי המעבד. כשבודקים בעיות בביצועים בדרך הזו, כדאי להתמקד קודם במסגרת שפועלת הכי הרבה זמן ולעבור למסגרות קטנות יותר.
כשמנתחים את הזמן שבו המכשיר נמצא במצב Runnable
, צריך לקחת בחשבון את החומרה של המכשיר.
האפליקציה שמוצגת פועלת במכשיר לביש עם שני מעבדים, ולכן צפוי שיהיה יותר זמן במצב Runnable
ויותר תחרות על המעבד עם תהליכים אחרים, מאשר אם היינו בודקים מכשיר עם יותר מעבדים. למרות שמשך הזמן שבו האפליקציה נמצאת במצב Runnable
ארוך יותר מהצפוי באפליקציית טלפון רגילה, זה יכול להיות מובן בהקשר של מכשירים לבישים.
משך הזמן בOpenDexFilesFromOat*
עכשיו בודקים את הזמן שחלף ב-OpenDexFilesFromOat*
. בנתוני המעקב, זה קורה באותו הזמן כמו הפלח bindApplication
. הפלח הזה מייצג את הזמן שלוקח לקרוא את קובצי ה-DEX של האפליקציה.
עסקאות חסומות של כרטיס מועדון
לאחר מכן בודקים את העסקאות של הכרטיס. עסקאות Binder מייצגות שיחות בין הלקוח לשרת: במקרה הזה, האפליקציה (הלקוח) קוראת למערכת Android (השרת) עם binder transaction
, והשרת מגיב עם binder
reply
. חשוב לוודא שהאפליקציה לא מבצעת עסקאות מיותרות של Binder במהלך ההפעלה, כי הן מגדילות את הסיכון להתנגשות בין תהליכים במעבד. אם אפשר,
כדאי לדחות את העבודה שכוללת קריאות ל-binder לתקופה שאחרי הפעלת האפליקציה. אם אתם צריכים לבצע עסקאות של Binder, ודאו שהן לא נמשכות יותר מקצב הרענון של Vsync במכשיר.
העסקה הראשונה של ה-binder, שמתרחשת בדרך כלל באותו זמן כמו ActivityThreadMain
slice, נראית ארוכה למדי במקרה הזה. כדי לקבל מידע נוסף על מה שקורה, פועלים לפי השלבים הבאים:
- כדי לראות את התשובה המשויכת של ה-binder ולקבל מידע נוסף על סדר העדיפויות של העסקה של ה-binder, לוחצים על פרוסת העסקה של ה-binder שרוצים לבדוק.
כדי לראות את התשובה של הכרטיסייה, עוברים לחלונית Current Selection ולוחצים על binder reply בקטע Following threads. בשדה Thread (שרשור) מופיע גם השרשור שבו מתקבלת התשובה של ה-binder, אם רוצים לנווט אליו באופן ידני. התשובה תופיע בתהליך אחר. מופיעה שורה שמקשרת בין הטרנזקציה של הכריכה לבין התשובה.
איור 5. מזהים את עסקאות ה-binder שמתרחשות במהלך הפעלת האפליקציה ומעריכים אם אפשר לדחות אותן. כדי לראות איך שרת המערכת מטפל בעסקת ה-Binder הזו, מצמידים את השרשורים Cpu 0 ו-Cpu 1 לחלק העליון של המסך.
מחפשים את התהליכים במערכת שמטפלים בתשובת ה-Binder. לשם כך, מחפשים את הפרוסות שכוללות את שם השרשור של תשובת ה-Binder. במקרה הזה, 'Binder:687_11 [2542]'. לוחצים על התהליכים הרלוונטיים במערכת כדי לקבל מידע נוסף על העסקה של הכריכה.
כדאי לעיין בתהליך המערכת הזה שמשויך לעסקת ה-Binder שמעניינת אותך ומתרחשת במעבד 0:

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

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

תוצאות
בעקבות הניתוח הזה, צוות Gmail Wear OS ביצע את השיפורים הבאים:
- הם הבחינו בהתנגשות במהלך הפעלת האפליקציה כשבדקו את פעילות המעבד, ולכן החליפו את אנימציית הספינר ששימשה לציון טעינת האפליקציה בתמונה סטטית אחת. בנוסף, הם האריכו את משך הזמן של מסך הפתיחה כדי לדחות את מצב הניצוץ, המצב השני של המסך שמשמש לציון שהאפליקציה נטענת, כדי לפנות משאבי CPU. השיפור הזה קיצר את זמן האחזור של הפעלת האפליקציה ב-50%.
- לאחר בדיקת הזמן שחלף ב-
OpenDexFilesFromOat*
וב-JIT activity, הם הפעילו את R8 כדי לשכתב את Baseline Profiles. השיפור הזה קיצר את זמן האחזור של הפעלת האפליקציה ב-20%.
ריכזנו כאן כמה טיפים מהצוות שיעזרו לכם לנתח את ביצועי האפליקציה ביעילות:
- הגדרת תהליך מתמשך שיכול לאסוף באופן אוטומטי עקבות ותוצאות. מומלץ להגדיר מעקב אוטומטי באפליקציה באמצעות השוואה לשוק.
- כדאי להשתמש בבדיקות A/B כדי לבדוק שינויים שלדעתכם ישפרו את המצב, ולדחות אותם אם הם לא משפרים. אפשר למדוד את הביצועים בתרחישים שונים באמצעות ספריית Macrobenchmark.
מידע נוסף זמין במשאבים הבאים:
- ביצועים: שימוש בפרופיל דגימה עם Systrace – MAD Skills
- ביצועים: תיעוד של עקבות ב-Profiler – MAD Skills