קובץ הזמנה הוא טכניקה עדכנית לאופטימיזציה של כלי הקישור. קבצים של הזמנות הם קובצי טקסט שמכילים סמלים שמייצגים פונקציות. מקשרים כמו lld משתמשים בקבצי הזמנה כדי להגדיר את הפונקציות בסדר מסוים. הקובצים הבינאריים או הספריות האלה עם סמלים מסודרים מצמצמים את שגיאות הדף ומשפרים את זמן ההפעלה של התוכנית, כי הסמלים נטענים ביעילות במהלך הפעלה קרה של התוכנית.
כדי להוסיף תכונות של קובץ הזמנה לאפליקציה, צריך לבצע את שלושת השלבים הבאים:
- יצירת פרופילים וקובץ מיפוי
- יצירת קובץ הזמנה מהפרופילים ומקובץ המיפוי
- שימוש בקובץ ההזמנה במהלך בניית גרסת build להפצה כדי להגדיר את הפריסה של הסמלים
יצירת קובץ הזמנה
כדי ליצור קובץ הזמנה, צריך לבצע שלושה שלבים:
- יצירת גרסה של האפליקציה עם כלי מדידה שכותבת את קובץ ההזמנה
- מריצים את האפליקציה כדי ליצור את הפרופילים
- עיבוד הפרופילים וקובץ המיפוי
יצירת גרסת build עם מכשור
הפרופילים נוצרים על ידי הפעלת בנייה עם מכשור של האפליקציה.
כדי ליצור בילד עם כלי מדידה, צריך להוסיף את -forder-file-instrumentation לדגלי הקומפיילר ולדגלי ה-linker, ולוודא ש--mllvm -orderfile-write-mapping=<filename>-mapping.txt נוסף רק לדגלי הקומפיילר.
דגל האינסטרומנטציה מאפשר אינסטרומנטציה של קובץ ההזמנה לצורך פרופיל וטוען את הספרייה הספציפית שדרושה לפרופיל.
לעומת זאת, דגל המיפוי פשוט מוציא את קובץ המיפוי שמציג את גיבוב MD5 לכל פונקציה בתוך הקובץ הבינארי או הספרייה.
בנוסף, חשוב להעביר דגל אופטימיזציה כלשהו מלבד -O0, כי דרוש דגל גם לדגל האינסטרומנטציה וגם לדגל המיפוי.
אם לא מועבר דגל אופטימיזציה, קובץ המיפוי לא נוצר וה-build עם המדידה עשוי להפיק גיבובים שגויים לקובץ הפרופיל.
ndk-build
חשוב לוודא שאתם מבצעים build עם APP_OPTIM=release כדי ש-ndk-build ישתמש במצב אופטימיזציה שאינו -O0. כשמבצעים קומפילציה באמצעות AGP, הפעולה הזו מתבצעת אוטומטית בגרסאות build של אפליקציות שמוכנות להפצה.
LOCAL_CFLAGS += \
-forder-file-instrumentation \
-mllvm -orderfile-write-mapping=mapping.txt \
LOCAL_LDFLAGS += -forder-file-instrumentation
CMake
חשוב להשתמש בCMAKE_BUILD_TYPE שונה מ-Debug כדי ש-CMake ישתמש במצב אופטימיזציה שונה מ--O0. כשמבצעים build באמצעות AGP, הפעולה הזו מתבצעת אוטומטית ב-release builds.
target_compile_options(orderfiledemo PRIVATE
-forder-file-instrumentation
-mllvm -orderfile-write-mapping=mapping.txt
)
target_link_options(orderfiledemo PRIVATE -forder-file-instrumentation)
מערכות build אחרות
מהדרים את הקוד באמצעות -forder-file-instrumentation -O1 -mllvm
-orderfile-write-mapping=mapping.txt.
לא חובה להשתמש בערך -O1, אבל אסור להשתמש בערך -O0.
השמטה של -mllvm -orderfile-write-mapping=mapping.txt בזמן הקישור.
לא צריך את כל הדגלים האלה בגרסת build להפצה, ולכן צריך לשלוט בהם באמצעות משתנה build. כדי לפשט את התהליך, אפשר להגדיר את הכול בקובץ CMakeLists.txt כמו בדוגמה שלנו.
יצירת ספרייה של קובצי הזמנות
בנוסף לדגלים, צריך להגדיר את קובץ הפרופיל, והקובץ הבינארי עם המכשור צריך להפעיל באופן מפורש כתיבה של פרופיל במהלך ההרצה שלו.
- מתקשרים אל
__llvm_profile_set_filename(PROFILE_DIR "/<filename>-%m.profraw")כדי להגדיר את נתיב הפרופיל. למרות שהארגומנט שהועבר הוא<filename>-%m.profraw, קובץ הפרופיל נשמר כ<filename>-%m.profraw.order. מוודאים שהאפליקציה יכולה לכתוב ב-PROFILE_DIRושיש לכם גישה לספרייה.- בגלל שמתבצע פרופיל של הרבה ספריות משותפות,
%mשימושי כי הוא מתרחב לחתימת מודול ייחודית לספרייה, וכתוצאה מכך נוצר פרופיל נפרד לכל ספרייה. בקישור הזה אפשר למצוא עוד מפרטים של תבניות.
- בגלל שמתבצע פרופיל של הרבה ספריות משותפות,
- צריך להתקשר אל
__llvm_profile_initialize_file()כדי להגדיר את קובץ הפרופיל - התקשרות אל
__llvm_orderfile_dump()כדי לכתוב במפורש לקובץ הפרופיל
הפרופילים נאספים בזיכרון והפונקציה dump כותבת אותם לקובץ. צריך לוודא שהפונקציה dump נקראת בסוף ההפעלה, כדי שקובץ הפרופיל יכלול את כל הסמלים עד סוף ההפעלה.
extern "C" {
extern int __llvm_profile_set_filename(const char*);
extern int __llvm_profile_initialize_file(void);
extern int __llvm_orderfile_dump(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_initialize_file();
__llvm_orderfile_dump();
return;
}
הפעלת הפיתוח של פרופילים
מריצים את האפליקציה עם האינסטרומנטציה במכשיר פיזי או וירטואלי כדי ליצור את הפרופילים.
אפשר לחלץ את קובצי הפרופיל באמצעות adb pull.
adb shell "run-as <package-name> sh -c 'cat /data/user/0/<package-name>/cache/default-%m.profraw.order' | cat > /data/local/tmp/default-%m.profraw.order"
adb pull /data/local/tmp/default-%m.profraw.order .
כמו שצוין קודם, חשוב לוודא שיש לכם גישה לתיקייה שמכילה את קובץ הפרופיל הכתוב. אם מדובר במכשיר וירטואלי, כדאי להימנע מהשימוש באמולטורים עם חנות Play, כי אין להם גישה לתיקיות רבות.
עיבוד לאחר יצירת הפרופיל וקובץ המיפוי
כשמקבלים את הפרופילים, צריך למצוא את קובץ המיפוי ולהמיר כל פרופיל לפורמט הקסדצימלי. בדרך כלל, אפשר למצוא את קובץ המיפוי בתיקיית ה-build של האפליקציה. אם יש לכם את שני הקבצים, תוכלו להשתמש בסקריפט שלנו כדי לקחת קובץ פרופיל ואת קובץ המיפוי הנכון וליצור קובץ הזמנה.
Linux/Mac/ChromeOS
hexdump -C default-%m.profraw.order > default-%m.prof
python3 create_orderfile.py --profile-file default-%m.prof --mapping-file <filename>-mapping.txt
Windows
certutil -f -encodeHex default-%m.profraw.order default-%m.prof
python3 create_orderfile.py --profile-file default-%m.prof --mapping-file <filename>-mapping.txt
אם רוצים לקרוא מידע נוסף על הסקריפט, אפשר לעיין בקובץ ה-README.
שימוש בקובץ הזמנה כדי לבנות אפליקציה
אחרי שיוצרים קובץ הזמנה, צריך להסיר את הדגלים הקודמים ואת הפונקציות של קובץ ההזמנה, כי הם מיועדים רק לשלבי היצירה.
צריך רק להעביר את -Wl,--symbol-ordering-file=<filename>.orderfile אל דגלי ההידור והמקשר.
לפעמים לא ניתן למצוא סמלים או להזיז אותם, ומוצגות אזהרות. כדי להסתיר את האזהרות האלה, אפשר להעביר את -Wl,--no-warn-symbol-ordering.
ndk-build
LOCAL_CFLAGS += \
-Wl,--symbol-ordering-file=<filename>.orderfile \
-Wl,--no-warn-symbol-ordering \
LOCAL_LDFLAGS += \
-Wl,--symbol-ordering-file=<filename>.orderfile \
-Wl,--no-warn-symbol-ordering \
CMake
target_compile_options(orderfiledemo PRIVATE
-Wl,--symbol-ordering-file=<filename>.orderfile
-Wl,--no-warn-symbol-ordering
)
target_link_options(orderfiledemo PRIVATE
-Wl,--symbol-ordering-file=<filename>.orderfile
-Wl,--no-warn-symbol-ordering
)
מערכות build אחרות
מהדרים את הקוד באמצעות -Wl,--symbol-ordering-file=<filename>.orderfile
-Wl,--no-warn-symbol-ordering.
מידע נוסף זמין בדוגמה של קובץ הזמנה.
פרטי הטמעה של קובץ הזמנה
יש הרבה דרכים ליצור קובצי הזמנות ולהשתמש בהם לבנייה. ה-NDK משתמש בשיטה של LLVM, ולכן הוא הכי שימושי לספריות המשותפות של C או C++ באפליקציה בפועל ב-Java או ב-Kotlin. Clang לוקח כל שם פונקציה (סמל), יוצר ממנו גיבוב MD5 ומציג את הקשר הזה בקובץ מיפוי. גיבוב MD5 של פונקציה נכתב בקובץ הפרופיל (בפורמט profraw) כשהפונקציה מופעלת בפעם הראשונה. הפעלות עוקבות של הפונקציה לא כותבות את הגיבוב MD5 שלה לקובץ הפרופיל, כי המטרה היא להימנע מכפילויות. כתוצאה מכך, רק ההפעלה הראשונה של הפונקציה מתועדת בסדר. אפשר לעבור על קובץ הפרופיל וקובץ המיפוי, לקחת כל גיבוב MD5 ולהחליף אותו בפונקציה המתאימה כדי לקבל קובץ הזמנה.
דוגמאות לקובץ פרופיל בפורמט הקסדצימלי ולקובץ מיפוי אפשר למצוא בקבצים example.prof ו-example-mapping.txt בהתאמה.