יצירה ומדידה ידנית של פרופילים בסיסיים

מומלץ מאוד ליצור כללי פרופיל באופן אוטומטי באמצעות ספריית Macrobenchmark של Jetpack כדי לצמצם את המאמץ הידני ולהגדיל את יכולת ההתאמה לעומס. עם זאת, אפשר ליצור כללי פרופיל באפליקציה ולמדוד אותם באופן ידני.

הגדרת כללי פרופיל באופן ידני

אפשר להגדיר כללי פרופיל באופן ידני באפליקציה או במודול ספרייה על ידי יצירת קובץ בשם baseline-prof.txt שנמצא בתיקייה src/main. זוהי אותה תיקייה שמכילה את הקובץ AndroidManifest.xml.

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

התחביר של הכללים האלה הוא קבוצת-על של פורמט הפרופיל של ART (HRF) שקריא לבני אדם כשמשתמשים ב-adb shell profman --dump-classes-and-methods. התחביר דומה לתחביר של מתארים וחתימות, אבל מאפשר להשתמש בתווים כלליים לחיפוש כדי לפשט את תהליך כתיבת הכללים.

בדוגמה הבאה מוצגים כמה כללים של פרופיל בסיס שכלולים בספריית Jetpack Compose:

HSPLandroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
HSPLandroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
HLandroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
PLandroidx/compose/runtime/CompositionImpl;->applyChanges()V
HLandroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
Landroidx/compose/runtime/ComposerImpl;

אפשר לנסות לשנות את כללי הפרופיל בפרויקט לדוגמה ב-Compiler Explorer. חשוב לזכור ש-Compiler Explorer תומך רק בפורמט הפרופיל של ART שאפשר לקרוא על ידי בני אדם (HRF), ולכן אין תמיכה בתווים כלליים.

תחביר של כללים

הכללים האלה יכולים להיות באחד משני פורמטים כדי לטרגט שיטות או כיתות:

[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]

כלל של כיתה משתמש בתבנית הבאה:

[CLASS_DESCRIPTOR]

תיאור מפורט זמין בטבלה הבאה:

תחביר תיאור
FLAGS מייצג תו אחד או יותר מבין התווים H,‏ S ו-P כדי לציין אם צריך לסמן את השיטה הזו בתור Hot,‏ Startup או Post Startup בהתאם לסוג ההפעלה.

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

שיטה עם הדגל S מציינת שמדובר בשיטה שנקראת במהלך ההפעלה.

שיטה עם הדגל P מציינת שזו שיטה שנקראת אחרי ההפעלה.

כיתה שמופיעה בקובץ הזה מציינת שהיא נמצאת בשימוש במהלך ההפעלה, ויש להקצות אותה מראש בערימה כדי למנוע את העלות של טעינת הכיתה. במהלך הידור ב-ART נעשה שימוש באסטרטגיות אופטימיזציה שונות, כמו הידור AOT של השיטות האלה וביצוע אופטימיזציות של פריסה בקובץ AOT שנוצר.
CLASS_DESCRIPTOR תיאור של הכיתה של השיטה המטורגטת. לדוגמה, ל-androidx.compose.runtime.SlotTable יש מתאר של Landroidx/compose/runtime/SlotTable;. האות L מתווספת כאן בהתאם לפורמט Dalvik Executable‏ (DEX).
METHOD_SIGNATURE החתימה של השיטה, כולל השם, סוגי הפרמטרים וסוג המידע המוחזר של השיטה. לדוגמה:

// LayoutNode.kt

fun isPlaced():Boolean {
// ...
}

ב-LayoutNode יש את החתימה isPlaced()Z.

התבניות האלה יכולות לכלול תווים כלליים לחיפוש כדי שכלל אחד יכלול כמה שיטות או כיתות. לקבלת עזרה מודרכת בכתיבה באמצעות תחביר של כללים ב-Android Studio, אפשר להיעזר בפלאגין Android Baseline Profiles.

דוגמה לכלל תו כללי עשויה להיראות כך:

HSPLandroidx/compose/ui/layout/**->**(**)**

סוגי הנתונים הנתמכים בכללים של פרופיל Baseline

הכללים של פרופיל הבסיס תומכים בסוגי הנתונים הבאים. פרטים על הסוגים האלה מופיעים במאמר פורמט Dalvik Executable‏ (DEX).

תו סוג תיאור
B בייט בייט חתום
C char מיקום תו (code point) של תו Unicode שמקודד ב-UTF-16
D double ערך נקודה צפה (floating-point) עם דיוק כפול
F float ערך נקודה צפה (floating-point) ברמת דיוק יחידה
I INT מספר שלם
J ארוך מספר שלם ארוך
S סרטון קצר סרטון Shorts חתום
V void ביטול
Z בוליאני נכון או לא נכון
L (שם הכיתה) reference מופע של שם כיתה

בנוסף, בספריות אפשר להגדיר כללים שמארזים בארטיפקטים של AAR. כשמפתחים קובץ APK שכולל את הפריטים האלה, הכללים ממוזגים יחד – בדומה לאופן שבו מתבצע המיזוג של המניפסט – ומקובצים לפרופיל ART בינארי קומפקטי שספציפי לקובץ ה-APK.

כשמשתמשים ב-APK במכשירים, הפרופיל הזה מאפשר ל-ART לבצע הידור AOT של קבוצת משנה ספציפית של האפליקציה בזמן ההתקנה ב-Android 9 (רמת API 28) או ב-Android 7 (רמת API 24) כשמשתמשים ב-ProfileInstaller.

איסוף ידני של פרופילים Baseline

אפשר ליצור פרופיל בסיס באופן ידני בלי להגדיר את ספריית Macrobenchmark, וליצור אוטומציות של ממשק המשתמש בתהליכי השימוש הקריטיים. מומלץ להשתמש בבדיקות ביצועים ברמת המאקרו, אבל לא תמיד אפשר לעשות זאת. לדוגמה, אם אתם משתמשים במערכת build שאינה Gradle, לא תוכלו להשתמש בפלאגין Baseline Profile של Gradle. במקרים כאלה, אפשר לאסוף באופן ידני כללים של פרופיל בסיס. קל יותר לעשות זאת אם משתמשים במכשיר או במהדר שפועלים עם API 34 ואילך. אפשר לעשות זאת גם ברמות API נמוכות יותר, אבל צריך הרשאת root ולהשתמש במהדר (emulator) שפועל עם קובץ אימג' של AOSP. כדי לאסוף כללים ישירות:

  1. מתקינים גרסה זמינה של האפליקציה במכשיר לבדיקה. כדי לקבל פרופיל מדויק, סוג ה-build של האפליקציה צריך להיות מותאם ל-R8 ולא ניתן לניפוי באגים.
  2. מוודאים שהפרופילים כבר לא עבר קומפילציה.

    API מגרסה 34 ואילך

    adb shell cmd package compile -f -m verify $PACKAGE_NAME
    adb shell pm art clear-app-profiles $PACKAGE_NAME
    

    API מגרסה 33 ומטה

    adb root
    adb shell cmd package compile --reset $PACKAGE_NAME
    

    אם ל-APK יש תלות בספרייה Profile Installer של Jetpack, הספרייה תיצור פרופיל בזמן ההפעלה הראשונה של ה-APK. הפעולה הזו עלולה להפריע לתהליך יצירת הפרופיל, לכן צריך להשבית אותה באמצעות הפקודה הבאה:

    adb shell am broadcast -a androidx.profileinstaller.action.SKIP_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
    

  3. מריצים את האפליקציה ומנווטים באופן ידני במסלולי חוויית המשתמש החיוניים שרוצים לאסוף עבורם פרופיל.
  4. מבקשים מ-ART לפמפם את הפרופילים. אם חבילת ה-APK שלכם תלויה בספריית Profile Installer של Jetpack, תוכלו להשתמש בה כדי לדגום את הפרופילים:

    adb shell am broadcast -a androidx.profileinstaller.action.SAVE_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
    adb shell am force-stop $PACKAGE_NAME
    
    אם אתם לא משתמשים ב-Profile Installer, תוכלו להעביר את הפרופילים באופן ידני במהלך ההפעלה של המהדר באמצעות הפקודה הבאה:

    adb root
    adb shell killall -s SIGUSR1 $PACKAGE_NAME
    adb shell am force-stop $PACKAGE_NAME
    

  5. ממתינים לפחות חמש שניות עד שהיצירה של הפרופיל תושלם.
  6. ממירים את הפרופילים הבינאריים שנוצרים לטקסט:

    API מגרסה 34 ואילך

    adb shell pm dump-profiles --dump-classes-and-methods $PACKAGE_NAME
    

    API מגרסה 33 ומטה

    קובעים אם נוצר פרופיל עזר או פרופיל נוכחי. פרופיל העזר נמצא במיקום הבא:

    /data/misc/profiles/ref/$$PACKAGE_NAME/primary.prof
    

    פרופיל קיים נמצא במיקום הבא:

    /data/misc/profiles/cur/0/$PACKAGE_NAME/primary.prof
    

    קובעים את המיקום של קובץ ה-APK:

    adb root
    adb shell pm path $PACKAGE_NAME
    

    מבצעים את ההמרה:

    adb root
    adb shell profman --dump-classes-and-methods --profile-file=$PROFILE_PATH --apk=$APK_PATH > /data/misc/profman/$PACKAGE_NAME-primary.prof.txt
    

  7. משתמשים ב-adb כדי לאחזר את הפרופיל שהועבר מהמכשיר:

    adb pull /data/misc/profman/$PACKAGE_NAME-primary.prof.txt PATH_TO_APP_MODULE/src/main/
    

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

מדידה ידנית של שיפורים באפליקציה

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

PACKAGE_NAME=com.example.app
# Force Stop App
adb shell am force-stop $PACKAGE_NAME
# Reset compiled state
adb shell cmd package compile --reset $PACKAGE_NAME
# Measure App startup
# This corresponds to `Time to initial display` metric.
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

בשלב הבא, מעבירים את פרופיל Baseline להתקנה ידנית.

# Unzip the Release APK first.
unzip release.apk
# Create a ZIP archive.
# The name should match the name of the APK.
# Copy `baseline.prof{m}` and rename it `primary.prof{m}`.
cp assets/dexopt/baseline.prof primary.prof
cp assets/dexopt/baseline.profm primary.profm
# Create an archive.
zip -r release.dm primary.prof primary.profm
# Confirm that release.dm only contains the two profile files:
unzip -l release.dm
# Archive:  release.dm
#   Length      Date    Time    Name
# ---------  ---------- -----   ----
#      3885  1980-12-31 17:01   primary.prof
#      1024  1980-12-31 17:01   primary.profm
# ---------                     -------
#                               2 files
# Install APK + Profile together.
adb install-multiple release.apk release.dm

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

# Check dexopt state.
adb shell dumpsys package dexopt | grep -A 1 $PACKAGE_NAME

בפלט צריך להופיע הודעה על כך שהחבילה קובצה:

[com.example.app]
  path: /data/app/~~YvNxUxuP2e5xA6EGtM5i9A==/com.example.app-zQ0tkJN8tDrEZXTlrDUSBg==/base.apk
  arm64: [status=speed-profile] [reason=install-dm]

עכשיו אפשר למדוד את ביצועי האפליקציה בזמן ההפעלה כמו קודם, אבל בלי לאפס את המצב המקודד. חשוב לוודא שלא מאפסים את המצב המהדר של החבילה.

# Force stop app
adb shell am force-stop $PACKAGE_NAME
# Measure app startup
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

פרופילים Baseline ו-profgen

בקטע הזה מוסבר מה הכלי profgen עושה כשמפתחים גרסה בינארית קומפקטית של פרופיל בסיס.

Profgen-cli עוזר ביצירת קובצי פרופיל, בבדיקה עצמית ובטרנספיילציה של פרופילי ART, כדי שאפשר יהיה להתקין אותם במכשירים מבוססי Android ללא קשר לגרסה של ערכת ה-SDK היעד.

Profgen-cli הוא ממשק CLI שמאגד את קובץ ה-HRF של פרופיל בסיס לפורמט המאגד שלו. ה-CLI מופיע גם במאגר cmdline-tools כחלק מ-Android SDK.

התכונות הבאות זמינות בהסתעפות studio-main:

➜ ../cmdline-tools/latest/bin
apkanalyzer
avdmanager
lint
profgen
retrace
screenshot2
sdkmanager

יצירת פרופילים בינאריים קומפקטיים באמצעות Profgen-cli

הפקודות הזמינות ב-Profgen-cli הן bin,‏ validate ו-dumpProfile. כדי לראות את הפקודות הזמינות, משתמשים ב-profgen --help:

profgen --help
Usage: profgen options_list
Subcommands:
    bin - Generate Binary Profile
    validate - Validate Profile
    dumpProfile - Dump a binary profile to a HRF

Options:
    --help, -h -> Usage info

משתמשים בפקודה bin כדי ליצור את הפרופיל הבינארי הקומפקטי. דוגמה לקריאה:

profgen bin ./baseline-prof.txt \
  --apk ./release.apk \
  --map ./obfuscation-map.txt \
  --profile-format v0_1_0_p \
  --output ./baseline.prof \

כדי לראות את האפשרויות הזמינות, משתמשים ב-profgen bin options_list:

Usage: profgen bin options_list
Arguments:
    profile -> File path to Human Readable profile { String }
Options:
    --apk, -a -> File path to apk (always required) { String }
    --output, -o -> File path to generated binary profile (always required)
    --map, -m -> File path to name obfuscation map { String }
    --output-meta, -om -> File path to generated metadata output { String }
    --profile-format, -pf [V0_1_0_P] -> The ART profile format version
      { Value should be one of [
         v0_1_5_s, v0_1_0_p, v0_0_9_omr1, v0_0_5_o, v0_0_1_n
        ]
      }
    --help, -h -> Usage info

הארגומנט הראשון מייצג את הנתיב אל HRF של baseline-prof.txt.

בנוסף, צריך לציין את הנתיב ל-build של גרסת המהדורה של קובץ ה-APK ומפת ערפול שמשמשת לערפול קובץ ה-APK כשמשתמשים ב-R8 או ב-Proguard. כך, profgen יכול לתרגם את סמלי המקור ב-HRF לשמות המעורפלים התואמים שלהם בזמן יצירת הפרופיל המהדר.

מאחר שפורמטים של פרופילים של ART לא תואמים לאחור או קדימה, צריך לציין פורמט פרופיל כדי ש-profgen יארוז מטא-נתונים של פרופיל (profm) שאפשר להשתמש בהם כדי לבצע המרה של פורמט פרופיל ART אחד לפורמט אחר במקרה הצורך.

פורמטים של פרופילים וגרסאות של פלטפורמות

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

פורמט הפרופיל גירסת פלטפורמה רמת ממשק API:
v0_1_5_s Android S+ 31+
v0_1_0_p Android גרסאות P,‏ Q ו-R 28-30
v0_0_9_omr1 Android O MR1 27
v0_0_5_o Android O 26
v0_0_1_n Android N 24-25

מעתיקים את קובצי הפלט baseline.prof ו-baseline.profm לתיקייה assets או dexopt ב-APK.

מפות ערפול

צריך לספק את מפת הטשטוש רק אם ב-HRF נעשה שימוש בסמלי מקור. אם קובץ ה-HRF נוצר מ-build של גרסה שהושחתה כבר, ואין צורך במיפוי, אפשר להתעלם מהאפשרות הזו ולהעתיק את הפלט לתיקייה assets או dexopt.

התקנה רגילה של פרופילי Baseline

בדרך כלל, פרופילים בסיסיים מועברים למכשיר באחת משתי דרכים.

שימוש ב-install-multiple עם DexMetadata

במכשירים עם API מגרסה 28 ואילך, לקוח Play מוריד את עומס העבודה של ה-APK ו-DexMetadata‏ (DM) של גרסת ה-APK שמותקנת. ה-DM מכיל את פרטי הפרופיל שמועברים ל-Package Manager במכשיר.

התקנת ה-APK וה-DM מתבצעת כחלק מסשן התקנה יחיד באמצעות קוד דומה לזה:

adb install-multiple base.apk base.dm

Jetpack ProfileInstaller

במכשירים עם Android מגרסה 29 ואילך, הספרייה Jetpack ProfileInstaller מספקת מנגנון חלופי להתקנה של פרופיל שארוז ב-assets או ב-dexopt אחרי התקנת קובץ ה-APK במכשיר. ProfileInstallReceiver או האפליקציה עצמה מפעילים את ProfileInstaller.

הספרייה ProfileInstaller מבצעת המרה של הפרופיל על סמך גרסת ה-SDK של מכשיר היעד, ומעתיקה את הפרופיל לספרייה cur במכשיר (ספריית טרום-פריסה ספציפית לחבילה לפרופילים של ART במכשיר).

כשהמכשיר לא פעיל, הפרופיל נאסף על ידי תהליך שנקרא bg-dexopt במכשיר.

טעינת פרופיל Baseline באופן צדדי

בקטע הזה נסביר איך להתקין פרופיל בסיס על סמך קובץ APK.

שידור עם androidx.profileinstaller

במכשירים עם API מגרסה 24 ואילך, אפשר לשדר פקודה להתקנת הפרופיל:

# Broadcast the install profile command - moves binary profile from assets
#     to a location where ART uses it for the next compile.
#     When successful, the following command prints "1":
adb shell am broadcast \
    -a androidx.profileinstaller.action.INSTALL_PROFILE \
    <pkg>/androidx.profileinstaller.ProfileInstallReceiver

# Kill the process
am force-stop <pkg>

# Compile the package based on profile
adb shell cmd package compile -f -m speed-profile <pkg>

ProfileInstaller לא נמצא ברוב חבילות ה-APK עם פרופילים בסיסיים – שנמצאים בכ-77,000 מתוך 450,000 האפליקציות ב-Play – אבל הוא נמצא כמעט בכל חבילת APK שמשתמשת ב-Compose. הסיבה לכך היא שספריות יכולות לספק פרופילים בלי להצהיר על תלות ב-ProfileInstaller. הוספת יחסי תלות בכל ספרייה עם פרופיל חלה החל מ-Jetpack.

שימוש ב-install-multiple עם profgen או DexMetaData

במכשירים עם API מגרסה 28 ואילך, אפשר להתקין פרופיל בסיס דרך הצד בלי שתצטרכו לכלול את הספרייה ProfileInstaller באפליקציה.

כדי לעשות זאת, משתמשים ב-Profgen-cli:

profgen extractProfile \
        --apk app-release.apk \
        --output-dex-metadata app-release.dm \
        --profile-format V0_1_5_S # Select based on device and the preceding table.

# Install APK and the profile together
adb install-multiple appname-release.apk appname-release.dm

כדי לתמוך בחלוקות של חבילות APK, צריך להריץ את השלבים הקודמים של חילוץ הפרופיל פעם לכל קובץ APK. בזמן ההתקנה, מעבירים כל קובץ APK וקובץ .dm משויך, ומוודאים שהשמות של ה-APK ושל .dm תואמים:

adb install-multiple appname-base.apk appname-base.dm \
appname-split1.apk appname-split1.dm

אימות

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

איך מעבירים את התוכן של פרופיל בינארי

כדי לבדוק את התוכן של גרסה בינארית קומפקטית של פרופיל בסיס, משתמשים באפשרות dumpProfile של Profgen-cli:

Usage: profgen dumpProfile options_list
Options:
    --profile, -p -> File path to the binary profile (always required)
    --apk, -a -> File path to apk (always required) { String }
    --map, -m -> File path to name obfuscation map { String }
    --strict, -s [true] -> Strict mode
    --output, -o -> File path for the HRF (always required) { String }
    --help, -h -> Usage info

dumpProfile זקוק לקובץ ה-APK כי הייצוג הבינארי הקומפקטי שומר רק את ההיסטים של ה-DEX, ולכן הוא זקוק להם כדי לשחזר את שמות הכיתות והשיטות.

מצב קפדני מופעל כברירת מחדל, והוא מבצע בדיקת תאימות של הפרופיל לקובצי ה-DEX ב-APK. אם אתם מנסים לנפות באגים בפרופילים שנוצרו באמצעות כלי אחר, יכול להיות שתקבלו שגיאות תאימות שמונעות מכם ליצור גרסת dump לצורך בדיקה. במקרים כאלה, אפשר להשבית את המצב הקפדני באמצעות --strict false. עם זאת, ברוב המקרים כדאי להשאיר את המצב הקפדני מופעל.

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