מערכת ה-build של Android מגרסה 13 וגרסאות קודמות תומכת בשימוש ב-profile-guided optimization (PGO) של Clang במודולים מקומיים של Android שיש להם כללי build של blueprint. בדף הזה מתוארים Clang PGO, האופן שבו יוצרים ומעדכנים באופן שוטף פרופילים ל-PGO, והאופן שבו משלבים את PGO עם מערכת ה-build (עם תרחיש לדוגמה).
הערה: במסמך הזה מתוארת השימוש ב-PGO בפלטפורמת Android. מידע נוסף על שימוש ב-PGO מאפליקציה ל-Android זמין בדף הזה.
מידע על Clang PGO
אפשר לבצע אופטימיזציה מבוססת-פרופיל ב-Clang באמצעות שני סוגים של פרופילים:
- פרופילים מבוססי-הטמעה נוצרים מתוכנית יעד עם הטמעה. הפרופילים האלה מפורטים ומגבירים את התקורה בסביבת זמן הריצה.
- פרופילים שמבוססים על דגימה נוצרים בדרך כלל על ידי ספירת אירועים בחומרה. הם יוצרים עומס נמוך בסביבת זמן הריצה, וניתן לאסוף אותם בלי כלים למדידת ביצועים או שינוי בקוד הבינארי. הם פחות מפורטים מאשר פרופילים שמבוססים על מכשירי מדידה.
צריך ליצור את כל הפרופילים מעומסי עבודה מייצגים שמפעילים את ההתנהגות הטיפוסית של האפליקציה. Clang תומך גם ב-AST מבוסס (-fprofile-instr-generate
) וגם ב-LLVM IR מבוסס (-fprofile-generate)
), אבל Android תומך רק ב-LLVM IR מבוסס ל-PGO מבוסס-הטמעה.
הדגלים הבאים נדרשים כדי ליצור אוסף פרופילים:
-fprofile-generate
לצורך אינסטרומנטציה מבוססת-IR. כשמשתמשים באפשרות הזו, הקצה העורפי משתמש בגישה של עץ מרחבי מינימלי עם משקל כדי לצמצם את מספר נקודות המדידה ולבצע אופטימיזציה של המיקום שלהן לקצוות עם משקל נמוך (צריך להשתמש באפשרות הזו גם בשלב הקישור). הנהג של Clang מעביר באופן אוטומטי את סביבת זמן הריצה של הפרופיל (libclang_rt.profile-arch-android.a
) למקשר. הספרייה הזו מכילה תוכניות לכתיבה של הפרופילים בדיסק כשהתוכנית יוצאת.-gline-tables-only
לאיסוף פרופילים שמבוסס על דגימה כדי ליצור מידע מינימלי על ניפוי באגים.
אפשר להשתמש בפרופיל ל-PGO באמצעות -fprofile-use=pathname
או -fprofile-sample-use=pathname
, לפרופילים שמבוססים על מכשירי מדידה ולפרופילים שמבוססים על דגימה, בהתאמה.
הערה: כשמבצעים שינויים בקוד, אם Clang לא יכולה להשתמש יותר בנתוני הפרופיל, היא יוצרת אזהרה מסוג -Wprofile-instr-out-of-date
.
שימוש ב-PGO
כדי להשתמש ב-PGO, צריך לבצע את השלבים הבאים:
- כדי ליצור את הספרייה או קובץ ההפעלה עם המדידה, מעבירים את הערך
-fprofile-generate
למהדר ולמקשר. - איסוף פרופילים על ידי הפעלת עומס עבודה מייצג בקובץ הבינארי המתווסף.
- עיבוד פוסט-פרוגרסיבי של הפרופילים באמצעות הכלי
llvm-profdata
(פרטים נוספים זמינים במאמר טיפול בקובצי פרופיל של LLVM). - כדי להשתמש בפרופילים כדי להחיל את PGO, מעבירים את הערך
-fprofile-use=<>.profdata
למהדר ולמקשר.
כדי לבצע אופטימיזציה ל-PGO ב-Android, צריך לאסוף את הפרופילים אופליין ולשלוח אותם ל-checkin יחד עם הקוד, כדי להבטיח שאפשר יהיה ליצור גרסאות build שניתנות לשכפול. אפשר להשתמש בפרופילים ככל שהקוד מתפתח, אבל צריך ליצור אותם מחדש מדי פעם (או בכל פעם ש-Clang מזהיר שהפרופילים לא מעודכנים).
איסוף פרופילים
Clang יכול להשתמש בפרופילים שנאספים על ידי הרצת מדדי ביצועים באמצעות build עם מכשירי מדידה בספרייה, או על ידי דגימה של מכשירי ספירה בחומרה כשמדד הביצועים פועל. בשלב זה, מערכת Android לא תומכת באיסוף פרופילים שמבוסס על דגימה, לכן צריך לאסוף פרופילים באמצעות build עם רכיבי מעקב:
- זיהוי של מדד ביצועים ושל קבוצת הספריות שמשמשות אותו באופן קולקטיבי.
- מוסיפים את המאפיינים
pgo
למדד ולספריות (פרטים בהמשך). - יוצרים גרסה זמינה ל-Android עם עותק של הספריות האלה עם כלי למדידת ביצועים באמצעות:
make ANDROID_PGO_INSTRUMENT=benchmark
benchmark
הוא placeholder שמזהה את האוסף של הספריות שנוספו להן כלים למדידת ביצועים במהלך ה-build. הקלטים שמייצגים את הבדיקה בפועל (ואולי קובץ הפעלה אחר שמקשר לספרייה שנבדקת) לא ספציפיים ל-PGO והם מחוץ להיקף של המסמך הזה.
- איך מעבירים או מסנכרנים את ה-build המתווסף למכשיר.
- מריצים את בדיקת הביצועים כדי לאסוף פרופילים.
- משתמשים בכלי
llvm-profdata
(כפי שמתואר בהמשך) כדי לעבד את הפרופילים לאחר היצירה שלהם ולהכין אותם להוספה לעץ המקורות.
שימוש בפרופילים במהלך ה-build
בודקים את הפרופילים ב-toolchain/pgo-profiles
בעץ Android. השם צריך להתאים לשם שמופיע במאפיין המשנה profile_file
של המאפיין pgo
בספרייה. מערכת ה-build מעבירה את קובץ הפרופיל ל-Clang באופן אוטומטי במהלך ה-build של הספרייה. אפשר להגדיר את משתנה הסביבה ANDROID_PGO_DISABLE_PROFILE_USE
לערך true
כדי להשבית את PGO באופן זמני ולמדוד את השיפור בביצועים.
כדי לציין עוד ספריות פרופיל ספציפיות למוצר, צריך לצרף אותן למשתנה PGO_ADDITIONAL_PROFILE_DIRECTORIES
ב-BoardConfig.mk
. אם יצוינו נתיבים נוספים, הפרופילים שבנתיבים האלה יבטלו את הפרופילים שב-toolchain/pgo-profiles
.
כשיוצרים קובץ אימג' של גרסה באמצעות היעד dist
ל-make
, מערכת ה-build כותבת את השמות של קובצי הפרופיל החסרים ב-$DIST_DIR/pgo_profile_file_missing.txt
. אפשר לבדוק את הקובץ הזה כדי לראות אילו קבצי פרופיל הוסרו בטעות (מה שמשבית את PGO ללא התראה).
הפעלת PGO בקובצי Android.bp
כדי להפעיל את PGO בקובצי Android.bp
של מודולים מקומיים, פשוט מציינים את המאפיין pgo
. לנכס הזה יש את נכסי המשנה הבאים:
נכס | תיאור |
---|---|
instrumentation
|
מגדירים את הערך כ-true ל-PGO באמצעות מכשירי מדידה. ברירת המחדל היא
false . |
sampling
|
מגדירים את הערך true ל-PGO באמצעות דגימה. ברירת המחדל היא
false . |
benchmarks
|
רשימה של מחרוזות. המודול הזה נוצר לצורך יצירת פרופילים אם אחת מהאפשרויות למדדי הביצועים ברשימה מצוינות באפשרות ה-build ANDROID_PGO_INSTRUMENT . |
profile_file
|
קובץ הפרופיל (ביחס ל-toolchain/pgo-profile ) לשימוש עם PGO. ה-build מזהיר שהקובץ הזה לא קיים על ידי הוספת הקובץ הזה ל-$DIST_DIR/pgo_profile_file_missing.txt
אלא אם הנכס enable_profile_use מוגדר ל-false או המשתנה build ANDROID_PGO_NO_PROFILE_USE מוגדר ל-true . |
enable_profile_use
|
מגדירים את הערך false אם לא רוצים להשתמש בפרופילים במהלך ה-build. אפשר להשתמש בהם במהלך האתחול כדי להפעיל את איסוף הפרופילים או להשבית באופן זמני את PGO. ברירת המחדל היא true . |
cflags
|
רשימת דגלים נוספים לשימוש במהלך build עם כלי למדידת ביצועים. |
דוגמה למודול עם PGO:
cc_library { name: "libexample", srcs: [ "src1.cpp", "src2.cpp", ], static: [ "libstatic1", "libstatic2", ], shared: [ "libshared1", ] pgo: { instrumentation: true, benchmarks: [ "benchmark1", "benchmark2", ], profile_file: "example.profdata", } }
אם מדדי הביצועים benchmark1
ו-benchmark2
משקפים את ההתנהגות של הספריות libstatic1
, libstatic2
או libshared1
, הנכס pgo
של הספריות האלה יכול לכלול גם את מדדי הביצועים. המודול defaults
ב-Android.bp
יכול לכלול מפרט pgo
משותף לקבוצה של ספריות, כדי להימנע מהצגה חוזרת של אותם כללי build בכמה מודולים.
כדי לבחור קובצי פרופיל שונים או להשבית באופן סלקטיבי את PGO לארכיטקטורה מסוימת, צריך לציין את המאפיינים profile_file
, enable_profile_use
ו-cflags
לכל ארכיטקטורה. דוגמה (עם יעד הארכיטקטורה מודגש באותיות מודגשות):
cc_library { name: "libexample", srcs: [ "src1.cpp", "src2.cpp", ], static: [ "libstatic1", "libstatic2", ], shared: [ "libshared1", ], pgo: { instrumentation: true, benchmarks: [ "benchmark1", "benchmark2", ], } target: { android_arm: { pgo: { profile_file: "example_arm.profdata", } }, android_arm64: { pgo: { profile_file: "example_arm64.profdata", } } } }
כדי לפתור הפניות לספריית זמן הריצה של הפרופיל במהלך יצירת פרופיל מבוסס-מכשירים, מעבירים את דגל ה-build -fprofile-generate
למקשר. ספריות סטטיות שנוספו להן מודולים ל-PGO, כל הספריות המשותפות וכל קובץ בינארי שתלוי ישירות בספרייה הסטטית צריכים לקבל גם הם מודולים ל-PGO. עם זאת, בספריות משותפות או בקובצי הפעלה כאלה אין צורך להשתמש בפרופילים של PGO, ואפשר להגדיר את המאפיין enable_profile_use
שלהם כ-false
.
מעבר להגבלה הזו, אפשר להחיל את PGO על כל ספרייה סטטית, ספרייה משותפת או קובץ הפעלה.
טיפול בקובצי פרופיל של LLVM
הפעלה של ספרייה או קובץ הפעלה עם כלי למדידת ביצועים יוצרת קובץ פרופיל בשם default_unique_id_0.profraw
ב-/data/local/tmp
(כאשר unique_id
הוא גיבוב מספרי ייחודי לספרייה הזו). אם הקובץ הזה כבר קיים, סביבת זמן הריצה של יצירת הפרופיל תמזג את הפרופיל החדש עם הפרופיל הישן בזמן כתיבת הפרופילים. חשוב לזכור שלמפתחי אפליקציות אין גישה ל-/data/local/tmp
. במקום זאת, הם צריכים להשתמש במקום כמו /storage/emulated/0/Android/data/packagename/files
.
כדי לשנות את המיקום של קובץ הפרופיל, מגדירים את משתנה הסביבה LLVM_PROFILE_FILE
במהלך זמן הריצה.
לאחר מכן משתמשים בכלי llvm-profdata
כדי להמיר את הקובץ .profraw
(ואולי גם למזג כמה קבצים מסוג .profraw
) לקובץ .profdata
:
llvm-profdata merge -output=profile.profdata <.profraw and/or .profdata files>
לאחר מכן אפשר להטמיע את profile.profdata
בעץ המקור לשימוש במהלך ה-build.
אם נטענים כמה ספריות או קובצי בינארי עם מודולים במהלך בדיקת הביצועים, כל ספרייה יוצרת קובץ .profraw
נפרד עם מזהה ייחודי נפרד. בדרך כלל אפשר למזג את כל הקבצים האלה לקובץ .profdata
אחד ולהשתמש בו ל-build של PGO. במקרים שבהם ספרייה מופעלת על ידי מדד ביצועים אחר, צריך לבצע אופטימיזציה של הספרייה באמצעות פרופילים משני מדדי הביצועים. במקרה כזה, האפשרות show
של llvm-profdata
שימושית:
llvm-profdata merge -output=default_unique_id.profdata default_unique_id_0.profraw llvm-profdata show -all-functions default_unique_id.profdata
כדי למפות מזהי unique_id לספריות נפרדות, מחפשים בפלט של show
לכל unique_id שם פונקציה שהוא ייחודי לספרייה.
מקרה לדוגמה: PGO ל-ART
במחקר המקרה מוצגת ART כדוגמה רלוונטית, אבל הוא לא תיאור מדויק של קבוצת הספריות בפועל שעבורן נוצר פרופיל ב-ART או של יחסי התלות ההדדית ביניהן.
המהדר dex2oat
מראש ב-ART תלוי ב-libart-compiler.so
, שגם הוא תלוי ב-libart.so
. סביבת זמן הריצה של ART מיושמת בעיקר ב-libart.so
. מדדי הביצועים של המהדר ושל סביבת זמן הריצה יהיו שונים:
Benchmark | ספריות עם פרופיל |
---|---|
dex2oat
|
dex2oat (קובץ הפעלה), libart-compiler.so ,
libart.so |
art_runtime
|
libart.so
|
- מוסיפים את מאפיין
pgo
הבא אלdex2oat
,libart-compiler.so
:pgo: { instrumentation: true, benchmarks: ["dex2oat",], profile_file: "dex2oat.profdata", }
- מוסיפים את המאפיין
pgo
הבא אלlibart.so
:pgo: { instrumentation: true, benchmarks: ["art_runtime", "dex2oat",], profile_file: "libart.profdata", }
- יוצרים גרסאות build עם כלי למדידת ביצועים למדדי השוואה
dex2oat
ו-art_runtime
באמצעות:make ANDROID_PGO_INSTRUMENT=dex2oat make ANDROID_PGO_INSTRUMENT=art_runtime
- מריצים את נקודות השוואה שמפעילות את
dex2oat
ו-art_runtime
כדי לקבל את הערכים הבאים:- שלושה קובצי
.profraw
מ-dex2oat
(dex2oat_exe.profdata
,dex2oat_libart-compiler.profdata
ו-dexeoat_libart.profdata
), שזוהו באמצעות השיטה שמתוארת בקטע טיפול בקובצי פרופיל של LLVM. art_runtime_libart.profdata
יחיד.
- שלושה קובצי
- יוצרים קובץ profdata משותף לקובץ ההפעלה
dex2oat
ול-libart-compiler.so
באמצעות:llvm-profdata merge -output=dex2oat.profdata \ dex2oat_exe.profdata dex2oat_libart-compiler.profdata
- כדי לקבל את הפרופיל של
libart.so
, משלבים את הפרופילים משני אמות המידה:llvm-profdata merge -output=libart.profdata \ dex2oat_libart.profdata art_runtime_libart.profdata
יכול להיות שהמספרים הגולמיים של
libart.so
משני הפרופילים יהיו שונים, כי מדדי הביצועים שונים במספר תרחישי הבדיקה ובמשך הזמן שבו הם פועלים. במקרה כזה, אפשר להשתמש במיזוג משוקל:llvm-profdata merge -output=libart.profdata \ -weighted-input=2,dex2oat_libart.profdata \ -weighted-input=1,art_runtime_libart.profdata
הפקודה שלמעלה מקצה למשקל של הפרופיל מ-
dex2oat
פי שניים. המשקל בפועל צריך להיקבע על סמך ידע בתחום או ניסוי. - בודקים את קובצי הפרופיל
dex2oat.profdata
ו-libart.profdata
ב-toolchain/pgo-profiles
לשימוש במהלך ה-build.
לחלופין, אפשר ליצור גרסה מצורפת של build עם כל הספריות באמצעות:
make ANDROID_PGO_INSTRUMENT=dex2oat,art_runtime (or) make ANDROID_PGO_INSTRUMENT=ALL
הפקודה השנייה יוצרת את כל המודולים שתומכים ב-PGO לצורך יצירת פרופיל.