순서 파일

순서 파일은 최근에 도입된 링커 최적화 기법입니다. 순서 파일은 함수를 나타내는 기호가 포함된 텍스트 파일입니다. lld와 같은 링커는 순서 파일을 사용하여 함수를 특정 순서로 배치합니다. 순서가 지정된 기호가 있는 바이너리 또는 라이브러리는 프로그램의 콜드 스타트 중에 기호가 효율적으로 로드되기 때문에 페이지 오류를 줄여 주고 프로그램의 시작 시간을 개선합니다.

아래의 3단계에 따라 애플리케이션에 순서 파일 기능을 추가할 수 있습니다.

  1. 프로필 및 매핑 파일 생성
  2. 프로필 및 매핑 파일에서 순서 파일 만들기
  3. 출시 빌드 중에 순서 파일을 사용하여 기호 배치

순서 파일 생성

순서 파일을 생성하려면 다음 세 단계를 진행해야 합니다.

  1. 순서 파일을 작성하는 계측 버전의 앱 빌드
  2. 앱을 실행하여 프로필 생성
  3. 프로필 및 매핑 파일 후처리

계측 빌드 만들기

프로필은 애플리케이션의 계측 빌드를 실행하여 생성됩니다. 계측 빌드의 경우 컴파일러 플래그와 링커 플래그 양쪽에 모두 -forder-file-instrumentation을 추가해야 하고 -mllvm -orderfile-write-mapping=<filename>-mapping.txt는 컴파일러 플래그에만 추가해야 합니다. 계측 플래그는 프로파일링을 위한 순서 파일 계측을 지원하며 프로파일링에 필요한 특정 라이브러리를 로드합니다. 반면 매핑 플래그는 바이너리 또는 라이브러리 내 각 함수의 MD5 해시를 보여주는 매핑 파일을 출력하기만 합니다.

계측 플래그와 매핑 플래그 모두 최적화 플래그가 필요하므로 -O0을 제외한 모든 최적화 플래그를 전달해야 합니다. 최적화 플래그를 전달하지 않으면 매핑 파일이 생성되지 않고 계측 빌드가 프로필 파일에 잘못된 해시를 출력할 수 있습니다.

ndk-build

ndk-build가 -O0이 아닌 최적화 모드를 사용하도록 APP_OPTIM=release를 사용하여 빌드해야 합니다. AGP를 사용하여 빌드할 때는 출시 빌드에서 자동으로 이 작업을 수행합니다.

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

LOCAL_LDFLAGS += -forder-file-instrumentation

CMake

CMake가 -O0이 아닌 최적화 모드를 사용하도록 Debug가 아닌 CMAKE_BUILD_TYPE을 사용해야 합니다. 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를 생략합니다.

출시 빌드에는 이러한 플래그가 필요하지 않으므로 빌드 변수를 사용하여 제어해야 합니다. 편의상 샘플에서와 같이 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 스토어가 있는 에뮬레이터는 여러 폴더에 액세스할 수 없으므로 사용하지 않는 것이 좋습니다.

프로필 및 매핑 파일 후처리

프로필을 가져올 때는 매핑 파일을 찾아서 각 프로필을 16진수 형식으로 변환해야 합니다. 매핑 파일은 일반적으로 앱의 빌드 폴더에서 찾을 수 있습니다. 두 파일이 모두 있으면 Google의 스크립트를 사용하여 프로필 파일과 올바른 매핑 파일로 순서 파일을 생성할 수 있습니다

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

스크립트에 관한 자세한 내용은 이 리드미를 확인하세요.

순서 파일을 사용하여 애플리케이션 빌드

순서 파일을 생성한 후에는 앞에서 사용한 플래그와 순서 파일 함수를 삭제해야 합니다. 플래그와 함수는 순서 파일을 생성하는 데만 필요한 데이터이기 때문입니다. -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
)

기타 빌드 시스템

-Wl,--symbol-ordering-file=<filename>.orderfile -Wl,--no-warn-symbol-ordering을 사용하여 코드를 컴파일합니다.

자세한 내용은 순서 파일 예를 참고하세요.

순서 파일 구현 세부정보

순서 파일을 생성하고 빌드에 사용하는 방법에는 여러 가지가 있습니다. NDK는 LLVM의 메서드를 사용하므로 실제 Java 또는 Kotlin 앱보다 C 또는 C++ 공유 라이브러리에 적합합니다. Clang은 모든 함수 이름(기호)을 가져와서 MD5 해시를 만들고 매핑 파일에 관계를 출력합니다. 함수의 MD5 해시는 함수가 처음으로 실행될 때 프로필 파일(profraw 형식)에 쓰여집니다. 이후에 함수를 실행하면 중복을 피하기 위해 MD5 해시가 프로필 파일에 쓰여지지 않습니다. 따라서 함수의 첫 번째 실행만 순서에 기록됩니다. 프로필 파일과 매핑 파일을 검토하여 각 MD5 해시를 대응되는 함수로 대체하고 순서 파일을 얻을 수 있습니다.

16진수 형식의 프로필 파일과 매핑 파일의 예는 각각 example.profexample-mapping.txt에서 확인할 수 있습니다.