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

מומלץ מאוד ליצור כללי פרופיל באופן אוטומטי באמצעות ספריית 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 נקודת קוד של תו Unicode בקידוד UTF-16
D כפול ערך נקודה צפה בדיוק כפול
F float ערך נקודה צפה ברמת דיוק יחידה
I INT מספר שלם
J ארוך מספר שלם ארוך
S סרטון קצר סרטון Shorts חתום
V ריק ביטול
Z בוליאני נכון או לא נכון
L (שם הכיתה) reference מופע של שם מחלקה

בנוסף, ספריות יכולות להגדיר כללים הארוזים בפריטי מידע שנוצרו בתהליך פיתוח (Artifact) של 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 תלוי בספרייה של מנהל ההתקנה של פרופיל Jetpack, צריך להשתמש בה כדי להציב את הפרופילים:

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

    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"

פרופילים ופרוגן בסיסיים

בקטע הזה מוסבר מה הכלי 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 של גרסה שכבר מעורפלת (obfuscated) ולא נדרש מיפוי, אפשר להתעלם מהאפשרות הזו ולהעתיק את הפלט לתיקייה assets או dexopt.

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

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

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

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

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

adb install-multiple base.apk base.dm

Jetpack ProfileInstaller

במכשירים עם API ברמה 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 עם פרוגן או DexMetaData

במכשירים עם API מגרסה 28 ואילך, אפשר לבצע התקנה ממקור לא ידוע של פרופיל Baseline בלי צורך בספריית 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

אימות

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

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

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

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. עם זאת, ברוב המקרים כדאי להשאיר את המצב הקפדני מופעל.

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