אפשר להשתמש ב-Simpleperf כדי להעריך את הביצועים של מכשיר. Simpleperf הוא כלי פרופילים מקורי לאפליקציות ולתהליכים מקוריים ב-Android. אפשר להשתמש ב-CPU Profiler כדי לבדוק את השימוש במעבד של האפליקציה ואת פעילות השרשור בזמן אמת.
יש שני אינדיקטורים של ביצועים שגלויים למשתמשים:
- ביצועים צפויים ומורגשים. האם ממשק המשתמש משמיט פריימים או שהוא מעבד באופן עקבי ב-60FPS? האם האודיו מושמע ללא ארטיפקטים או רעשי פצפוץ? כמה זמן עובר מהרגע שבו המשתמש נוגע במסך ועד שהאפקט מוצג בתצוגה?
- משך הזמן הנדרש לפעולות ארוכות יותר (כמו פתיחת אפליקציות).
הראשון בולט יותר מהשני. המשתמשים בדרך כלל מבחינים ב-jank, אבל הם לא יוכלו להבחין בין זמן הפעלה של 500 אלפיות השנייה לבין זמן הפעלה של 600 אלפיות השנייה של האפליקציה, אלא אם הם מסתכלים על שני מכשירים זה לצד זה. השהיית המגע מורגשת באופן מיידי ותורמת באופן משמעותי לתפיסת המכשיר.
לכן, במכשיר מהיר, צינור הנתונים של ממשק המשתמש הוא הדבר הכי חשוב במערכת, מלבד מה שנדרש כדי לשמור על הפונקציונליות של צינור הנתונים של ממשק המשתמש. המשמעות היא שצינור הנתונים של ממשק המשתמש צריך לקבל עדיפות על פני כל עבודה אחרת שלא נחוצה כדי שממשק המשתמש יפעל בצורה חלקה. כדי לשמור על ממשק משתמש חלק, צריך לדחות את הסנכרון ברקע, את שליחת ההתראות ופעולות דומות אחרות אם אפשר להריץ פעולות בממשק המשתמש. אפשר לפגוע בביצועים של פעולות ארוכות יותר (זמן הריצה של HDR+, הפעלת האפליקציה וכו') כדי לשמור על ממשק משתמש חלק.
קיבולת לעומת ג'יטר
כשבודקים את ביצועי המכשיר, הקיבולת והתנודות הם שני מדדים חשובים.
קיבולת
קיבולת היא הכמות הכוללת של משאב מסוים שיש למכשיר במשך פרק זמן מסוים. יכול להיות שמדובר במשאבי מעבד (CPU), משאבי GPU, משאבי קלט/פלט, משאבי רשת, רוחב פס של זיכרון או כל מדד דומה. כשבודקים את הביצועים של המערכת כולה, יכול להיות שיהיה שימושי להפריד בין הרכיבים השונים ולהניח שיש מדד יחיד שקובע את הביצועים (במיוחד כשמבצעים אופטימיזציה למכשיר חדש, כי סביר להניח שעומסי העבודה שמופעלים במכשיר קבועים).
הקיבולת של מערכת משתנה בהתאם למשאבי המחשוב אונליין. שינוי התדירות של CPU או GPU הוא האמצעי העיקרי לשינוי הקיבולת, אבל יש גם אמצעים אחרים, כמו שינוי מספר ליבות ה-CPU אונליין. בהתאם לכך, הקיבולת של מערכת תואמת לצריכת החשמל שלה. שינוי הקיבולת תמיד יוביל לשינוי דומה בצריכת החשמל.
הקיבולת הנדרשת בזמן נתון נקבעת בעיקר על ידי האפליקציה שפועלת. כתוצאה מכך, הפלטפורמה יכולה לעשות מעט מאוד כדי להתאים את הקיבולת הנדרשת לעומס עבודה נתון, והאמצעים לעשות זאת מוגבלים לשיפורים בזמן הריצה (מסגרת Android, 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. ג'יטר הוא התנהגות אקראית של המערכת שמונעת הפעלה של עבודה מורגשת. לרוב מדובר בעבודה שצריך להריץ, אבל יכול להיות שאין לה דרישות תזמון מחמירות שגורמות לה לפעול בזמן מסוים. מכיוון שהתופעה אקראית, קשה מאוד להפריך את קיומה בעומס עבודה נתון. קשה מאוד להוכיח שמקור ידוע של ג'יטר היה הגורם לבעיה ספציפית בביצועים. הכלים שמשמשים בדרך כלל לאבחון הסיבות לשינויים בתזמון (כמו מעקב או רישום ביומן) יכולים לגרום לשינויים משלהם בתזמון.
מקורות של ג'יטר שנחווה בהטמעות של Android בעולם האמיתי כוללים:
- עיכוב בתזמון
- רכיבי handler של הפרעות
- קוד של מנהל התקן פועל יותר מדי זמן עם השבתה של קדימות או הפרעות
- Long-running softirqs
- תחרות על נעילה (אפליקציה, מסגרת, מנהל התקן של ליבת המערכת, נעילת binder, נעילת mmap )
- מצב של תחרות על מתאר קובץ, שבו שרשור בעדיפות נמוכה מחזיק את הנעילה של קובץ, ומונע משרשור בעדיפות גבוהה לפעול
- הפעלת קוד קריטי לממשק המשתמש בתורי עבודה שבהם יכול להיות עיכוב
- מעברים של מעבד למצב המתנה
- רישום
- עיכובים בקלט/פלט
- יצירת תהליכים מיותרים (לדוגמה, שידורים של
CONNECTIVITY_CHANGE
) - Page cache thrashing caused by insufficient free memory
משך הזמן הנדרש לתקופה מסוימת של ג'יטר עשוי לקטון או לא לקטון ככל שהקיבולת גדלה. לדוגמה, אם נהג משאיר את ההפרעות מושבתות בזמן שהוא מחכה לקריאה מאוטובוס i2c, ייקח לו פרק זמן קבוע, בלי קשר אם המעבד הוא 384 MHz או 2 GHz. הגדלת הקיבולת היא לא פתרון אפשרי לשיפור הביצועים כשמעורב ג'יטר. כתוצאה מכך, מעבדים מהירים יותר בדרך כלל לא ישפרו את הביצועים במצבים שבהם יש מגבלות על הג'יטר.
לבסוף, בניגוד לקיבולת, ג'יטר הוא כמעט כולו בתחום של ספק המערכת.
צריכת זיכרון
באופן מסורתי, צריכת הזיכרון נחשבת לגורם לביצועים ירודים. השימוש בזיכרון כשלעצמו הוא לא בעיה בביצועים, אבל הוא יכול לגרום לשינויים לא סדירים בנתוני השהייה (Jitter) בגלל תקורה של lowmemorykiller, הפעלה מחדש של שירותים ושימוש מוגזם במטמון של הדף. צמצום צריכת הזיכרון יכול למנוע את הגורמים הישירים לביצועים נמוכים, אבל יכול להיות שיש שיפורים ממוקדים אחרים שימנעו את הגורמים האלה גם כן (לדוגמה, הצמדת המסגרת כדי למנוע את החלפת הדפים שלה אם היא תוחלף בקרוב).
ניתוח הביצועים הראשוניים של המכשיר
התחלה ממערכת פונקציונלית אבל עם ביצועים נמוכים וניסיון לתקן את התנהגות המערכת על ידי בחינת מקרים ספציפיים של ביצועים נמוכים שגלויים למשתמשים לא נחשבת לאסטרטגיה טובה. הסיבה לכך היא שביצועים ירודים בדרך כלל לא ניתנים לשחזור בקלות (כלומר, יש תנודות), או שהם נובעים מבעיה באפליקציה. יותר מדי משתנים במערכת המלאה מונעים מהאסטרטגיה הזו להיות יעילה. כתוצאה מכך, קל מאוד לטעות בזיהוי הגורמים ולבצע שיפורים קלים, תוך פספוס הזדמנויות מערכתיות לשיפור הביצועים בכל המערכת.
במקום זאת, צריך לפעול לפי הגישה הכללית הבאה כשמפעילים מכשיר חדש:
- מפעילים את המערכת עד לממשק המשתמש, כשכל מנהלי ההתקנים פועלים וכמה הגדרות בסיסיות של בקר תדירות מוגדרות (אם משנים את ההגדרות של בקר התדירות, צריך לחזור על כל השלבים שבהמשך).
- מוודאים שהליבה תומכת בנקודת המעקב
sched_blocked_reason
וגם בנקודות מעקב אחרות בצינור העיבוד של התצוגה, שמציינות מתי המסגרת מועברת לתצוגה. - מבצעים מעקב ארוך של צינור הנתונים המלא של ממשק המשתמש (מקבלת הקלט דרך IRQ ועד לסריקה הסופית) בזמן הפעלת עומס עבודה קל ועקבי (לדוגמה, UiBench או בדיקת הכדור ב-TouchLatency).
- צריך לתקן את הנטישות של פריימים שזוהו בעומס העבודה הקל והעקבי.
- חוזרים על שלבים 3-4 עד שאפשר להריץ את הסרטון בלי שאף פריים יישמט למשך 20 שניות ומעלה בכל פעם.
- עוברים למקורות אחרים של ג'אנק שגלויים למשתמשים.
דברים פשוטים נוספים שאפשר לעשות בשלב מוקדם בהפעלת המכשיר:
- מוודאים שהליבה כוללת את תיקון sched_blocked_reason של נקודת המעקב. נקודת המעקב הזו מופעלת באמצעות קטגוריית המעקב sched ב-systrace, ומספקת את הפונקציה שאחראית על ההמתנה כששרשור נכנס למצב המתנה שלא ניתן להפריע לו. הוא חיוני לניתוח הביצועים, כי שינה ללא הפרעות היא אינדיקטור נפוץ מאוד לשינויים מהירים.
- מוודאים שיש לכם מספיק נתוני מעקב עבור צינורות הנתונים של ה-GPU והתצוגה. במערכות Qualcomm SOC עדכניות, נקודות המעקב מופעלות באמצעות:
adb shell "echo 1 > /d/tracing/events/kgsl/enable"
adb shell "echo 1 > /d/tracing/events/mdss/enable"
האירועים האלה נשארים מופעלים כשמריצים את systrace, כדי שתוכלו לראות מידע נוסף על צינור התצוגה (MDSS) במעקב בקטע mdss_fb0
. ב-SOC של Qualcomm, לא מוצג מידע נוסף על ה-GPU בתצוגת systrace הרגילה, אבל התוצאות מופיעות ב-trace עצמו (לפרטים, אפשר לעיין במאמר הסבר על systrace).
מה שרוצים לקבל ממעקב כזה אחרי הצגת מסגרת הוא אירוע יחיד שמציין באופן ישיר שמסגרת נמסרה לתצוגה. מכאן אפשר לקבוע אם הצלחתם להגיע לזמן הפריים הרצוי. אם האירוע Xn מתרחש פחות מ-16.7 אלפיות השנייה אחרי האירוע Xn-1 (בהנחה שהתצוגה היא 60 Hz), סימן שלא היה ג'יטר. אם ה-SOC שלכם לא מספק אותות כאלה, צריך לפנות לספק כדי לקבל אותם. קשה מאוד לנפות באגים של גמגום בלי אות חד-משמעי של השלמת פריים.
שימוש בבדיקות השוואה סינתטיות
נקודות מידוד סינתטיות שימושיות כדי לוודא שהפונקציונליות הבסיסית של המכשיר קיימת. עם זאת, אין טעם להתייחס לנקודות מידוד כאל תחליף לביצועי המכשיר בפועל.
על סמך ניסיון עם SOC, ההבדלים בביצועים של מדד השוואה סינתטי בין SOC לא קשורים להבדל דומה בביצועים של ממשק משתמש מורגש (מספר הפריימים שהושמטו, זמן הפריימים באחוזון ה-99 וכו'). נקודות השוואה סינתטיות הן נקודות השוואה של קיבולת בלבד. הג'יטר משפיע על הביצועים הנמדדים של נקודות ההשוואה האלה רק על ידי גניבת זמן מהפעולה הכוללת של נקודת ההשוואה. כתוצאה מכך, ציוני השוואה סינתטיים הם ברוב המקרים לא רלוונטיים כמדד לביצועים מנקודת המבט של המשתמש.
נניח שיש שני SOC שמריצים את Benchmark X ומעבדים 1,000 פריימים של ממשק משתמש, ומדווחים על זמן העיבוד הכולל (ציון נמוך יותר הוא טוב יותר).
- SOC 1 מעבד כל פריים של Benchmark X ב-10 אלפיות השנייה ומקבל ציון של 10,000.
- מערכת SOC 2 מעבדת 99% מהפריימים ב-1 אלפית השנייה, אבל 1% מהפריימים ב-100 אלפיות השנייה, והציון שלה הוא 19,900 – ציון טוב בהרבה.
אם מדד ההשוואה מייצג את הביצועים בפועל של ממשק המשתמש, אי אפשר יהיה להשתמש ב-SOC 2. בהנחה שקצב הרענון הוא 60 Hz, ב-SOC 2 יהיה פריים מגומגם כל 1.5 שניות של פעולה. לעומת זאת, SOC 1 (ה-SOC האיטי יותר לפי Benchmark X) יהיה חלק לחלוטין.
שימוש בדוחות על באגים
דוחות על באגים יכולים להיות שימושיים לפעמים לניתוח ביצועים, אבל בגלל שהם כבדים מאוד, הם לא שימושיים בדרך כלל לניפוי באגים בבעיות של גמגום ספורדי. יכול להיות שהם יספקו רמזים לגבי מה שהמערכת עשתה בזמן מסוים, במיוחד אם הבעיה הייתה קשורה למעבר בין אפליקציות (שנרשם בדוח באגים). דוחות על באגים יכולים גם להצביע על בעיה רחבה יותר במערכת, שיכולה להפחית את הקיבולת האפקטיבית שלה (למשל, ויסות תרמי או פיצול של הזיכרון).
שימוש ב-TouchLatency
כמה דוגמאות להתנהגות לא תקינה מגיעות מ-TouchLatency, שהוא עומס העבודה התקופתי המועדף שמשמש ל-Pixel ול-Pixel XL. הכלי זמין בכתובת frameworks/base/tests/TouchLatency
ויש לו שני מצבים: זמן האחזור של המגע וכדור קופץ (כדי לעבור בין המצבים, לוחצים על הלחצן בפינה השמאלית העליונה).
מבחן הכדור הקופץ הוא פשוט בדיוק כמו שהוא נראה: כדור קופץ על המסך לנצח, ללא קשר לקלט המשתמש. בדרך כלל, זה גם המבחן הכי קשה להרצה בצורה מושלמת, אבל ככל שהמכשיר יצליח להריץ אותו בלי להפיל פריימים, כך הוא יהיה טוב יותר. הבדיקה של הכדור הקופץ קשה כי היא עומס עבודה טריוויאלי אבל עקבי לחלוטין שפועל בשעון נמוך מאוד (בהנחה שלמכשיר יש בקר תדרים; אם המכשיר פועל עם שעונים קבועים, צריך להוריד את התדר של המעבד או ה-GPU כמעט למינימום כשמריצים את הבדיקה של הכדור הקופץ בפעם הראשונה). כשהמערכת נכנסת למצב השהיה והשעונים מתקרבים למצב סרק, הזמן הנדרש של יחידת העיבוד המרכזית (CPU) או של ה-GPU לכל פריים גדל. אפשר לצפות בכדור ולראות את התנועה המקוטעת, וגם לראות את הפריימים החסרים ב-systrace.
מכיוון שעומס העבודה עקבי מאוד, קל יותר לזהות את רוב המקורות של תנודות מאשר ברוב עומסי העבודה שגלויים למשתמשים, על ידי מעקב אחרי מה בדיוק פועל במערכת במהלך כל פריים שהוחמץ, במקום אחרי צינור הנתונים של ממשק המשתמש. השעונים הנמוכים מגבירים את ההשפעות של הג'יטר, כי יש סיכוי גבוה יותר שג'יטר יגרום להשמטת פריימים. כתוצאה מכך, ככל שערך TouchLatency קרוב יותר ל-60FPS, כך הסיכויים להתנהגויות מערכת לא תקינות שגורמות לבעיות זמניות שקשה לשחזר באפליקציות גדולות יותר נמוכים יותר.
הג'יטר לא תלוי בדרך כלל (אבל לא תמיד) במהירות השעון, ולכן כדאי להשתמש בבדיקה שפועלת במהירויות שעון נמוכות מאוד כדי לאבחן את הג'יטר, מהסיבות הבאות:
- לא כל הג'יטר הוא בלתי תלוי במהירות השעון. מקורות רבים צורכים רק זמן מעבד.
- ה-governor אמור להוריד את מהירות השעון כדי להקטין את הזמן הממוצע של הפריים קרוב למועד האחרון, כך שהזמן שמוקדש להפעלת עבודה שלא קשורה לממשק המשתמש יכול להוביל לחריגה מהמועד האחרון ולפספוס פריים.