سفارش فایل ها

فایل ترتیبی (Order file) یک تکنیک بهینه‌سازی پیونددهنده (linker) جدید است. این فایل‌های ترتیبی، فایل‌های متنی حاوی نمادهایی هستند که نشان‌دهنده توابع هستند. پیونددهنده‌هایی مانند lld از فایل‌های ترتیبی برای طرح‌بندی توابع به ترتیب خاص استفاده می‌کنند. این فایل‌های باینری یا کتابخانه‌هایی با نمادهای ترتیبی، خطاهای صفحه را کاهش داده و زمان راه‌اندازی برنامه را به دلیل بارگذاری کارآمد نمادها در طول شروع سرد (cold-start) برنامه بهبود می‌بخشند.

ویژگی‌های فایل سفارش را می‌توان با دنبال کردن سه مرحله به برنامه خود اضافه کرد:

  1. تولید پروفایل و فایل نقشه برداری
  2. ایجاد یک فایل سفارش از پروفایل‌ها و فایل نقشه‌برداری
  3. از فایل سفارش در طول ساخت نسخه آزمایشی برای طرح‌بندی نمادها استفاده کنید

ایجاد فایل سفارش

ایجاد فایل سفارش به سه مرحله نیاز دارد:

  1. یک نسخه ابزاری از برنامه بسازید که فایل سفارش را می‌نویسد
  2. برنامه را اجرا کنید تا پروفایل‌ها تولید شوند
  3. پس‌پردازش پروفیل‌ها و فایل نقشه‌برداری

یک ساخت ابزار دقیق ایجاد کنید

پروفایل‌ها با اجرای یک نسخه ابزاربندی‌شده از برنامه تولید می‌شوند. یک نسخه ابزاربندی‌شده نیاز به اضافه کردن -forder-file-instrumentation به هر دو پرچم کامپایلر و لینکر دارد، به طوری که -mllvm -orderfile-write-mapping=<filename>-mapping.txt به طور دقیق به پرچم‌های کامپایلر اضافه شود. پرچم ابزاربندی، ابزاربندی order file را برای پروفایل‌بندی فعال می‌کند و کتابخانه خاص مورد نیاز برای پروفایل‌بندی را بارگذاری می‌کند. از سوی دیگر، پرچم نگاشت فقط فایل نگاشتی را که هش MD5 را برای هر تابع در فایل باینری یا کتابخانه نشان می‌دهد، خروجی می‌دهد.

علاوه بر این، مطمئن شوید که هر پرچم بهینه‌سازی به جز -O0 را ارسال می‌کنید، زیرا هم پرچم ابزار دقیق و هم پرچم نگاشت به آن نیاز دارند. اگر هیچ پرچم بهینه‌سازی ارسال نشود، فایل نگاشت تولید نمی‌شود و ممکن است ساختار ابزار دقیق، هش‌های اشتباهی را به فایل پروفایل ارسال کند.

ساخت ndk

مطمئن شوید که با APP_OPTIM=release ساخته می‌شود، بنابراین ndk-build از حالت بهینه‌سازی دیگری غیر از -O0 استفاده می‌کند. هنگام ساخت با AGP، این حالت برای نسخه‌های آزمایشی به صورت خودکار اعمال می‌شود.

LOCAL_CFLAGS += \
    -forder-file-instrumentation \
    -mllvm -orderfile-write-mapping=mapping.txt \

LOCAL_LDFLAGS += -forder-file-instrumentation

سی‌میک

حتماً از CMAKE_BUILD_TYPE غیر از Debug استفاده کنید تا CMake از حالت بهینه‌سازی غیر از -O0 استفاده کند. هنگام ساخت با AGP، این حالت برای نسخه‌های آزمایشی به صورت خودکار فعال می‌شود.

target_compile_options(orderfiledemo PRIVATE
    -forder-file-instrumentation
    -mllvm -orderfile-write-mapping=mapping.txt
)
target_link_options(orderfiledemo PRIVATE -forder-file-instrumentation)

سایر سیستم‌های ساخت

کد خود را با استفاده از -forder-file-instrumentation -O1 -mllvm -orderfile-write-mapping=mapping.txt ‎ کامپایل کنید.

-O1 به طور خاص مورد نیاز نیست، اما -O0 هم استفاده نکنید.

هنگام لینک کردن -mllvm -orderfile-write-mapping=mapping.txt ‎ را حذف کنید.

همه این پرچم‌ها برای ساخت نسخه آزمایشی (Release build) مورد نیاز نیستند، بنابراین باید توسط یک متغیر ساخت (build variable) کنترل شوند. برای سادگی، می‌توانید همه این موارد را مانند نمونه ما در 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;
}

اجرای Build برای پروفایل‌ها

برنامه‌ی ابزارسازی‌شده را روی یک دستگاه فیزیکی یا مجازی اجرا کنید تا پروفایل‌ها تولید شوند. می‌توانید فایل‌های پروفایل را با استفاده از 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 Store به دلیل عدم دسترسی به بسیاری از پوشه‌ها اجتناب کنید.

پس‌پردازش پروفایل و فایل نقشه‌برداری

وقتی پروفایل‌ها را دریافت کردید، باید فایل نگاشت را پیدا کنید و هر پروفایل را به فرمت هگزادسیمال تبدیل کنید. معمولاً می‌توانید فایل نگاشت را در پوشه ساخت برنامه پیدا کنید. وقتی هر دو را دارید، می‌توانید از اسکریپت ما برای گرفتن یک فایل پروفایل و فایل نگاشت صحیح برای تولید یک فایل سفارش استفاده کنید.

لینوکس/مک/سیستم‌عامل کروم

hexdump -C default-%m.profraw.order > default-%m.prof
python3 create_orderfile.py --profile-file default-%m.prof --mapping-file <filename>-mapping.txt

ویندوز

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 را بررسی کنید.

استفاده از فایل Order برای ساخت برنامه

پس از تولید یک فایل سفارش، باید پرچم‌های قبلی و توابع فایل سفارش را حذف کنید زیرا آنها فقط برای مراحل تولید در نظر گرفته شده‌اند. شما فقط باید -Wl,--symbol-ordering-file=<filename>.orderfile به پرچم‌های کامپایل و لینکر ارسال کنید. گاهی اوقات، نمادها پیدا نمی‌شوند یا نمی‌توانند جابجا شوند و هشدار می‌دهند، بنابراین می‌توانید -Wl,--no-warn-symbol-ordering برای سرکوب این هشدارها ارسال کنید.

ساخت ndk

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 \

سی‌میک

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
)

سایر سیستم‌های ساخت

کد خود را با استفاده از -Wl,--symbol-ordering-file=<filename>.orderfile -Wl,--no-warn-symbol-ordering ‎ کامپایل کنید.

برای اطلاعات بیشتر، نمونه فایل سفارش را بررسی کنید.

جزئیات اجرای فایل سفارش

روش‌های زیادی برای تولید فایل‌های ترتیب و استفاده از آنها برای ساخت وجود دارد. NDK از روش LLVM استفاده می‌کند، بنابراین برای کتابخانه‌های مشترک C یا C++ شما نسبت به برنامه واقعی جاوا یا کاتلین مفیدترین است. Clang نام هر تابع (نماد) را می‌گیرد و یک هش MD5 از آن ایجاد می‌کند و این رابطه را به یک فایل نگاشت خروجی می‌دهد. هش MD5 یک تابع هنگام اجرای تابع برای اولین بار در فایل پروفایل (با فرمت profraw) نوشته می‌شود. هرگونه اجرای بعدی تابع، هش MD5 آن را در فایل پروفایل نمی‌نویسد زیرا می‌خواهد از تکرار جلوگیری کند. در نتیجه، فقط اولین اجرای تابع در ترتیب ثبت می‌شود. با بررسی فایل پروفایل و فایل نگاشت، می‌توانید هر هش MD5 را گرفته و آن را با تابع مربوطه جایگزین کنید و یک فایل سفارش دریافت کنید.

نمونه‌هایی از هر دو فایل پروفایل در قالب هگزادسیمال و فایل نگاشت را می‌توان به ترتیب در example.prof و example-mapping.txt یافت.