אופטימיזציה מודרכת באמצעות פרופיל

אופטימיזציה מודרכת (PGO) היא שיטת אופטימיזציה ידועה של מהדר לעיבוד טקסט. ב-PGO, אנחנו משתמשים בפרופילים של זמן ריצה מהפעלות של תוכנית מהדר (compiler) כדי לקבל החלטות אופטימליות לגבי ההטבעה ופריסת הקוד. זה מוביל אל ביצועים משופרים וקוד קטן יותר.

אפשר לפרוס את PGO באפליקציה או בספרייה באמצעות השלבים הבאים: 1. מזהים עומס עבודה מייצג. 2. אי��וף פרופילים. 3. שימוש בפרופילים בגרסת ה-build של הגרסה.

שלב 1: זיהוי עומס עבודה מייצג

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

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

שלב 2: איסוף פרופילים

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

יצירה מוזיקלית

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

יצירת פרופילים

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

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

  • כדי להגדיר את הנתיב לקובץ הפרופיל, יש להתקשר __llvm_profile_set_filename(PROFILE_DIR "/default-%m.profraw %m מועיל כשיש כמה ספריות משותפות. %m' מתרחב למודול ייחודי לחתימה, וכתוצאה מכך נוצר פרופיל נפרד לכל ספרייה. צפייה כאן למזהי תבניות שימושיים נוספים. PROFILE_DIR היא ספרייה ניתן לכתיבה מהאפליקציה. לצפייה בהדגמה לזיהוי הספרייה הזו בזמן הריצה.
  • כדי להפעיל באופן מפורש כתיבה בפרופיל, צריך להפעיל את __llvm_profile_write_file מותאמת אישית.
extern "C" {
extern int __llvm_profile_set_filename(const char*);
extern int __llvm_profile_write_file(void);
}

#define PROFILE_DIR "<location-writable-from-app>"
void workload() {
  // ...
  // run workload
  // ...

  // set path and write profiles after workload execution
  __llvm_profile_set_filename(PROFILE_DIR "/default-%m.profraw");
  __llvm_profile_write_file();
  return;
}

הערה: קל יותר ליצור את קובץ הפרופיל אם עומס העבודה הוא קובץ בינארי עצמאי – פשוט מגדירים את משתנה הסביבה LLVM_PROFILE_FILE ל-%t/default-%m.profraw לפני הרצת הקוד הבינארי.

פרופילים לאחר התהליך

קובצי הפרופיל הם בפורמט .profraw. קודם צריך לאחזר אותם מ: במכשיר באמצעות adb pull. לאחר השליפה, יש להשתמש בכלי השירות llvm-profdata ב- את ה-NDK כדי להמיר מ-.profraw ל-.profdata, ואז אפשר להעביר אותו מהדר (compiler) .

$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-profdata \
    merge --output=pgo_profile.profdata \
    <list-of-profraw-files>

כדי להימנע מהגרסה, צריך להשתמש ב-llvm-profdata וב-clang מאותה גרסת NDK אי-התאמה בין הפורמטים של קובץ הפרופיל.

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

שימוש בפרופיל מהשלב הקודם במהלך גרסת ה-build של על ידי העברת -fprofile-use=<>.profdata למהדר ולמקשר. אפשר להשתמש בפרופילים גם כשהקוד הולך ומתפתח – המהדר של Clang יכול לסבול חוסר התאמה קל בין המקור לפרופילים.

הערה: באופן כללי, הפרופילים ברוב הספריות נפוצים בכל הארכיטקטורות. לדוגמה, אם יוצרים פרופילים שנוצרו מ-builder של Arm64 של הספרייה, אפשר להשתמש בהם בשביל את כל הארכיטקטורות. הסיגנות היא שאם יש ארכיטקטורה ספציפית, בנתיבי ה��וד בספרייה (זרוע לעומת x86 או 32 סיביות לעומת 64 ביט), פרופילים נפרדים צריך לשמש לכל הגדרה כזו.

סיכום של כל המידע

https://github.com/DanAlbert/ndk-Sample/tree/pgo/pgo מציגה הדגמה מקצה לקצה לשימוש ב-PGO באפליקציה. הוא מספק פרטים שדולגו במסמך הזה.

  • גרסת CMake כללים מראים איך להגדיר משתנה CMake שיוצר קוד נייטיב עם אינסטרומנטציה. כשמשתנה ה-build לא מוגדר, מתבצעת אופטימיזציה של קוד ה-Native באמצעות פרופילי PGO שנוצרו.
  • בפיתוח עם אינסטרומנטציה, pgodemo.cpp כותב שהפרופילים הם ביצוע של עומס עבודה (workload).
  • מיקום ניתן לכתיבה עבור הפרופילים מתקבל בזמן הריצה ב- MainActivity.kt באמצעות applicationContext.cacheDir.toString().
  • כדי לשלוף פרופילים מהמכשיר בלי לדרוש adb root, צריך להשתמש בadb מתכון כאן.