משתמשים ב-Simpleperf כדי להעריך את הביצועים של מכשיר. Simpleperf הוא כלי פרופיל מקורי לאפליקציות ולתהליכים מקומיים ב-Android. אפשר להשתמש ב-Profiler למעבד כדי לבדוק את השימוש של האפליקציה במעבד ואת פעילות השרשור בזמן אמת.
יש שני אינדיקטורים של ביצועים שגלויים למשתמשים:
- ביצועים צפויים וחושבים. האם בממשק המשתמש (UI) יש ירידה בפריימים או שהוא מבצע רינדור באופן עקבי בקצב של 60FPS? האם האודיו מופעל ללא בעיות או רעשי פופ? מהו משך העיכוב בין המגע של המשתמש במסך לבין הצגת האפקט במסך?
- משך הזמן הנדרש לביצוע פעולות ארוכות יותר (כמו פתיחת אפליקציות).
האפשרות הראשונה בולטת יותר מהאפשרות השנייה. בדרך כלל המשתמשים מבחינים בתנודות, אבל הם לא יוכלו להבחין בין זמן הפעלה של אפליקציה של 500 אלפיות שנייה לבין זמן הפעלה של 600 אלפיות שנייה, אלא אם הם יבדקו שני מכשירים זה לצד זה. זמן האחזור של המגע מורגש באופן מיידי ומשפיע באופן משמעותי על התפיסה של המכשיר.
כתוצאה מכך, במכשיר מהיר צינור עיבוד הנתונים של ממשק המשתמש הוא הדבר החשוב ביותר במערכת, מלבד מה שדרוש כדי לשמור על פונקציונליות של צינור עיבוד הנתונים של ממשק המשתמש. כלומר, צינור עיבוד הנתונים של ממשק המשתמש צריך לדחות כל עבודה אחרת שלא נדרשת לממשק משתמש חלק. כדי לשמור על ממשק משתמש חלק, אם אפשר להריץ משימות של ממשק משתמש, צריך לעכב את כל העבודות האחרות, כמו סנכרון ברקע, שליחת התראות ועבודות דומות. מותר להתפשר על הביצועים של פעולות ארוכות יותר (זמן ריצה של HDR+, הפעלת אפליקציה וכו') כדי לשמור על ממשק משתמש חלק.
קיבולת לעומת רעידות
כשבודקים את ביצועי המכשיר, שני מדדים משמעותיים הם הספק והתנודות.
קיבולת
הקיבולת היא הכמות הכוללת של משאב מסוים שיש במכשיר לאורך פרק זמן מסוים. יכולים להיות אלה משאבי מעבד (CPU), משאבי GPU, משאבי קלט/פלט, משאבי רשת, רוחב פס של זיכרון או כל מדד דומה. כשבודקים את הביצועים של המערכת כולה, כדאי להתייחס לרכיבים השונים באופן מופשט ולהניח שמדד אחד קובע את הביצועים (במיוחד כשמתבצעת התאמה של מכשיר חדש, כי עומסי העבודה שפועלים במכשיר הזה הם קבועים).
הקיבולת של מערכת משתנה בהתאם למשאבי המחשוב אונליין. שינוי התדר של המעבד או של המעבד הגרפי הוא הדרך העיקרית לשינוי הקיבולת, אבל יש גם דרכים אחרות, כמו שינוי מספר ליבות המעבד אונליין. לכן, הקיבולת של מערכת תואמת לצריכת החשמל שלה. שינוי הקיבולת תמיד גורם לשינוי דומה בצריכת החשמל.
הקיבולת הנדרשת בכל רגע נתון נקבעת בעיקר על ידי האפליקציה שפועלת. כתוצאה מכך, הפלטפורמה יכולה לעשות מעט מאוד כדי לשנות את הקיבולת הנדרשת לכל עומס עבודה נתון, והדרכים לעשות זאת מוגבלות לשיפורים בסביבת זמן הריצה (Android framework, ART, Bionic, GPU compiler/drivers, kernel).
רעידות
קל לראות את הקיבולת הנדרשת של עומס עבודה, אבל המושג 'רעידה' הוא מושג מעורפל יותר. למידע נוסף על תנודות קצב השעון כמכשול לקבלת ביצועים מהירים, מומלץ לקרוא את המאמר The Case of the Missing Supercomputer Performance: Achieving Optimal Performance on the 8,192 processors of ASCI Q. (המחקר הזה בוחן למה המחשב העל-רלוונטי ASCI Q לא השיג את הביצועים הצפויים, והוא מבוא מצוין לאופטימיזציה של מערכות גדולות).
בדף הזה נעשה שימוש במונח 'רעידה' כדי לתאר את מה שמכונה במאמר ASCI Q בשם רעש. רעידות הן התנהגות מערכת אקראית שמונעת את הפעלת העבודה. לרוב מדובר בעבודה שצריך להריץ, אבל יכול להיות שאין לה דרישות זמן קפדניות שמחייבות אותה לפעול בזמן מסוים. מכיוון שהיא אקראית, קשה מאוד להפריך את קיומו של רעידות בעומס עבודה נתון. בנוסף, קשה מאוד להוכיח שמקור ידוע של רעידות היה הגורם לבעיה ספציפית בביצועים. הכלים הנפוצים ביותר לאבחון הגורמים לתנודות (כמו מעקב או רישום ביומן) עלולים לגרום לתנודות משלהם.
מקורות של רעידות (jitter) שנובעים מהטמעות של Android בעולם האמיתי כוללים:
- עיכוב של מתזמן המשימות
- רכיבי handler להפסקות
- קוד הנהג פועל במשך זמן רב מדי כשהקצאת עדיפות או ההפרעות מושבתות
- אירועי softirqs ממושכים
- תחרות על נעילה (אפליקציה, מסגרת, מנהל ליבה, נעילת binder, נעילת mmap)
- תחרות על מתאר קובץ, שבה שרשור בעדיפות נמוכה מחזיק את הנעילה על קובץ, ומונע משרשור בעדיפות גבוהה לפעול
- הרצת קוד קריטי לממשק המשתמש בקטעי עבודה שבהם הוא עלול להתעכב
- מעברים של מעבדים (CPU) למצב חוסר פעילות
- רישום
- עיכובים בקלט/פלט
- יצירת תהליכים מיותרים (לדוגמה, שידורים של
CONNECTIVITY_CHANGE
) - טרחה מיותרת במטמון הדפים שנגרמת בגלל חוסר בזיכרון פנוי
משך הזמן הנדרש לתקופה נתונה של תנודות יכול להתקצר או לא ככל שהקיבולת גדלה. לדוגמה, אם הנהג משאיר את ההפרעות מושבתות בזמן ההמתנה לקריאה באוטובוס i2c, הזמן שיידרש יהיה קבוע, ללא קשר למהירות המעבד – 384MHz או 2GHz. הגדלת הקיבולת היא לא פתרון ריאלי לשיפור הביצועים כשיש תנודות באיכות האות. כתוצאה מכך, בדרך כלל מעבדים מהירים יותר לא ישפרו את הביצועים במצבים שבהם יש הגבלות על תנודות זמן האחזור (jitter).
לבסוף, בניגוד לקיבולת, התנודות בזמן ההגעה (jitter) נמצאות כמעט לחלוטין בתחום של ספק המערכת.
צריכת זיכרון
בדרך כלל, צריכת הזיכרון היא הגורם הבולט לבעיות בביצועים. צריכת הנתונים עצמה היא לא בעיית ביצועים, אבל היא עלולה לגרום לתנודות (jitter) כתוצאה מעומסי ניהול של lowmemorykiller, הפעלות מחדש של השירות ושימוש מוגזם במטמון הדפים. צמצום צריכת הזיכרון יכול למנוע את הגורמים הישירים לביצועים חלשים, אבל יכולים להיות שיפורים ממוקדים אחרים שגם הם מונעים את הגורמים האלה (לדוגמה, הצמדת המסגרת כדי למנוע את ההוצאה שלה מהזיכרון, כי היא תוחזר אליו זמן קצר לאחר מכן).
ניתוח הביצועים הראשוניים של המכשיר
לא מומלץ להתחיל ממערכת שפועלת אבל הביצועים שלה נמוכים, ולנסות לתקן את התנהגות המערכת על ידי בדיקת מקרים ספציפיים של ביצועים נמוכים שגלויים למשתמשים. בדרך כלל קשה לשחזר ביצועים נמוכים (כלומר, רעידות) או בעיה באפליקציה, ולכן יש יותר מדי משתנים במערכת המלאה שמונעים מהשיטה הזו להיות יעילה. כתוצאה מכך, קל מאוד לזהות באופן שגוי את הגורמים ולבצע שיפורים קלים, תוך החמצה של הזדמנויות מערכתיות לשיפור הביצועים ברחבי המערכת.
במקום זאת, מומלץ להשתמש בגישה הכללית הבאה כשמפעילים מכשיר חדש:
- מפעילים את המערכת עד לממשק המשתמש, כשכל מנהלי ההתקנים פועלים ויש כמה הגדרות בסיסיות של פקטור הבקרה של התדר (אם משנים את הגדרות פקטור הבקרה של התדר, צריך לחזור על כל השלבים הבאים).
- מוודאים שהליבה תומכת בנקודת המעקב
sched_blocked_reason
וגם בנקודות מעקב אחרות בצינור עיבוד הנתונים של המסך, שמציינות מתי המסגרת נשלחת למסך. - יצירת מעקב ארוך של צינור עיבוד הנתונים של ממשק המשתמש כולו (מקבלת הקלט דרך IRQ ועד לסריקה הסופית) בזמן הרצת עומס עבודה קל ועקבי (לדוגמה, UiBench או בדיקת הכדור ב-TouchLatency).
- תיקון ירידות בפריימים שזוהו בעומס העבודה הקליל והעקבי.
- חוזרים על שלבים 3-4 עד שאפשר להריץ את הסרטון בלי פריימים שהוחמצו למשך יותר מ-20 שניות בכל פעם.
- עוברים למקורות אחרים של תנודות גליות שגלויות למשתמש.
דברים פשוטים אחרים שאפשר לעשות בשלב מוקדם של הפעלת המכשיר:
- מוודאים שהליבת המעבד כוללת את התיקון של tracepoint של sched_blocked_reason. נקודת המעקב הזו מופעלת באמצעות קטגוריית המעקב sched ב-systrace, והיא מספקת את הפונקציה שאחראית להעברה למצב שינה כשהשרשור נכנס למצב שינה ללא הפסקה. היא חיונית לניתוח הביצועים, כי שינה ללא הפסקה היא אינדיקטור נפוץ מאוד לתנודות.
- מוודאים שיש לכם מספיק מעקב אחרי צינורות עיבוד הנתונים של ה-GPU והתצוגה. ב-SOCs של Qualcomm מהדורות האחרונות, נקודות המעקב מופעלות באמצעות:
adb shell "echo 1 > /d/tracing/events/kgsl/enable"
adb shell "echo 1 > /d/tracing/events/mdss/enable"
האירועים האלה נשארים מופעלים כשמריצים את systrace, כך שאפשר לראות מידע נוסף על צינור עיבוד הנתונים של התצוגה (MDSS) בקטע mdss_fb0
. ב-SOCs של Qualcomm לא יוצג מידע נוסף על ה-GPU בתצוגה הרגילה של systrace, אבל התוצאות מופיעות ב-trace עצמו (פרטים נוספים זמינים במאמר הסבר על systrace).
מה שרוצים מהמעקב הזה אחרי המסך הוא אירוע יחיד שמציין ישירות שמסגרת נשלחה למסך. לאחר מכן תוכלו לקבוע אם הגעתם לזמן הפריימים הרצוי. אם האירוע Xn מתרחש תוך פחות מ-16.7 אלפיות השנייה אחרי האירוע Xn-1 (בהנחה שהמסך פועל בתדר של 60Hz), סימן שלא הייתה תנועה קטועה. אם ה-SOC לא מספק אותות כאלה, צריך לפנות לספק כדי לקבל אותם. קשה מאוד לנפות באגים של רעידות בלי אות סופי על השלמת הפריים.
שימוש במדדי ביצועים סינתטיים
מדדי ביצועים סינתטיים מאפשרים לוודא שהפונקציונליות הבסיסית של המכשיר קיימת. עם זאת, לא מומלץ להתייחס לנקודות השוואה כאל מדד לביצועי המכשיר כפי שהם נתפסים.
על סמך ניסיון עם מעבדי SOC, הבדלים בביצועים של מעבדי SOC במבחני ביצועים סינתטיים לא תמיד משקפים הבדלים דומים בביצועים של ממשק המשתמש (מספר הפריימים שהוחמצו, זמן הפריים ב-99 percentile וכו'). נקודות השוואה סינתטיות הן נקודות השוואה של קיבולת בלבד. התנודות בזמן האחזור משפיעות על הביצועים שנמדדים בנקודות השוואה האלה רק על ידי גזילה של זמן מהפעולה הראשית של נקודת השוואה. כתוצאה מכך, ציונים של מדדי ביצועים סינתטיים הם ברוב המקרים לא רלוונטיים כמדד של ביצועים כפי שהמשתמשים תופסים אותם.
נניח שיש שני מערכי SOC שפועלים במסגרת בדיקת הביצועים X, שמרינדרים 1,000 פריימים של ממשק משתמש ומדווחים על זמן הרינדור הכולל (ציון נמוך יותר הוא טוב יותר).
- SOC 1 מעבד כל פריים של מדד X ב-10 אלפיות השנייה ומקבל ציון של 10,000.
- ב-SOC 2, 99% מהפריימים עוברים רינדור תוך 1 אלפית השנייה, אבל 1% מהפריימים עוברים רינדור תוך 100 אלפיות השנייה, והציון הוא 19,900 – ציון טוב בהרבה.
אם מדד העזרה מציין את הביצועים בפועל של ממשק המשתמש, לא ניתן יהיה להשתמש ב-SOC 2. בהנחה של קצב ריענון של 60Hz, ב-SOC 2 תהיה תנודות בפריים בכל 1.5 שניות של פעולה. לעומת זאת, SOC 1 (ה-SOC האיטי יותר לפי מדד X) יפעל בצורה חלקה.
שימוש בדוחות על באגים
דוחות באגים יכולים להיות מועילים לפעמים לניתוח ביצועים, אבל בגלל שהם כבדים מאוד, הם לא מועילים בדרך כלל לניפוי באגים של בעיות תנודות תנועה פתאומית (jank) נדירות. הם עשויים לספק רמזים לגבי הפעולות שהמערכת ביצעה בזמן נתון, במיוחד אם התנודות היו סביב מעבר בין אפליקציות (שמתועד בדוח באג). דוחות באגים יכולים גם להצביע על בעיה רחבה יותר במערכת שעלולה לצמצם את הקיבולת האפקטיבית שלה (למשל, הגבלת קצב העברת הנתונים כתוצאה מהתחממות או פיצול זיכרון).
שימוש ב-TouchLatency
כמה דוגמאות להתנהגות לא תקינה מגיעות מ-TouchLatency, שהוא עומס העבודה המחזורי המועדף ב-Pixel וב-Pixel XL. הכלי זמין בכתובת frameworks/base/tests/TouchLatency
ויש לו שני מצבים: זמן אחזור למגע ו'כדור קופץ' (כדי לעבור בין המצבים, לוחצים על הלחצן בפינה השמאלית העליונה).
בדיקת הכדור הקופץ פשוטה בדיוק כפי שהיא נראית: כדור קופץ במסך לנצח, ללא קשר להזנת המשתמש. בדרך כלל, זה גם הבדיקה הקשה ביותר להרצה מושלמת, אבל ככל שהבדיקה תתקרב להרצה ללא פריימים שהוחמצו, כך המכשיר יהיה טוב יותר. בדיקת הכדור הקופץ היא קשה כי היא עומס עבודה פשוט אבל עקבי לחלוטין שפועל בקצב שעון נמוך מאוד (ההנחה היא שלמכשיר יש מנהל תדרים. אם המכשיר פועל במקום זאת עם שעונים קבועים, צריך להוריד את קצב השעון של המעבד/ה-GPU לקרוב למינימום כשמריצים את בדיקת הכדור הקופץ בפעם הראשונה). ככל שהמערכת נכנסת למצב רגיעה והשעונים מתקרבים למצב חוסר פעילות, הזמן הנדרש ל-CPU/GPU לכל פריים עולה. תוכלו לראות את הכדור ולראות תנודות, וגם לראות פריימים חסרים ב-systrace.
מכיוון שעומס העבודה עקבי מאוד, אפשר לזהות בקלות רבה יותר את רוב מקורות התנודות (jitter) בהשוואה לרוב עומסי העבודה שגלויים למשתמשים. לשם כך, עוקבים אחרי מה שמריצים במערכת במהלך כל פריים שהוחמצ, במקום אחרי צינור עיבוד הנתונים של ממשק המשתמש. שעונים איטיים יותר מגבירים את ההשפעות של רעידות על ידי הגדלת הסיכוי שרעידה כלשהי תגרום לירידה בפריים. כתוצאה מכך, ככל ש-TouchLatency קרוב יותר ל-60FPS, כך יש פחות סיכוי להתנהגויות מערכת לא טובות שגורמות לתנודות תנועה פתאומות (jank) באפליקציות גדולות, שקשה לשחזר.
מכיוון שתנודות קצב הנתונים (jitter) לרוב (אבל לא תמיד) לא משתנות בהתאם למהירות השעון, מומלץ להשתמש בבדיקות שפועלות במהירויות שעון נמוכות מאוד כדי לאבחן את תנודות קצב הנתונים מהסיבות הבאות:
- לא כל התנודות בזמן האות הן קבועות במהירות השעון. מקורות רבים פשוט צורכים זמן מעבד.
- כדי שהזמן הממוצע להצגת פריימים יהיה קרוב למועד היעד, ה-governor צריך להאט את השעון. לכן, הזמן שבו מתבצעת עבודה שלא קשורה לממשק המשתמש עלול לגרום לכך שה-governor יפסיק להציג פריימים.