PGO(프로필 기반 최적화) 사용

Android 빌드 시스템은 청사진 빌드 규칙이 있는 네이티브 Android 모듈에서 Clang의 PGO(프로필 기반 최적화) 사용을 지원합니다. 이 페이지에서는 Clang PGO를 설명하고, PGO에 사용되는 프로필을 지속적으로 생성 및 업데이트하는 방법과 PGO를 빌드 시스템과 통합하는 방법을 사용 사례를 들어 알아봅니다.

주의: 이 문서에서는 Android 플랫폼에서 PGO를 사용하는 방법을 설명합니다. Android 앱의 PGO 사용에 관해 알아보려면 이 페이지를 방문하세요.

Clang PGO 정보

Clang은 두 가지 유형의 프로필을 사용하여 프로필 기반 최적화를 실행할 수 있습니다.

  • 계측 기반 프로필은 계측 대상 프로그램에서 생성됩니다. 이러한 프로필은 상세 정보를 담고 있으며 높은 런타임 오버헤드를 부과합니다.
  • 샘플링 기반 프로필은 일반적으로 하드웨어 카운터를 샘플링하여 생성됩니다. 이 프로필은 런타임 오버헤드가 낮으며 바이너리를 계측 또는 수정하지 않고도 수집할 수 있습니다. 샘플링 기반 프로필보다는 계측 기반 프로필이 더 자세합니다.

모든 프로필은 애플리케이션의 일반적인 동작을 실행하는 대표 워크로드에서 생성되어야 합니다. Clang은 AST 기반(-fprofile-instr-generate)과 LLVM IR 기반(-fprofile-generate))을 모두 지원하지만 Android는 계측 기반 PGO의 LLVM IR 기반만 지원합니다.

프로필 수집을 위해 빌드하려면 다음 플래그가 필요합니다.

  • -fprofile-generate: IR 기반 계측의 경우. 이 옵션을 사용하면 백엔드에서 가중치 적용 최소 스패닝 트리 방식을 사용하여 계측 지점 수를 줄이고 위치를 가중치가 낮은 에지로 최적화합니다(링크 단계에서도 이 옵션 사용). Clang 드라이버는 프로파일링 런타임(libclang_rt.profile-arch-android.a)을 자동으로 링커에 전달합니다. 이 라이브러리에는 프로그램 종료 시 프로필을 디스크에 쓰는 루틴이 포함되어 있습니다.
  • -gline-tables-only: 최소 디버그 정보를 생성하기 위해 샘플링 기반 프로필을 수집하는 경우

계측 기반 프로필과 샘플링 기반 프로필의 -fprofile-instr-use=pathname 또는 -fprofile-sample-use=pathname을 각각 사용하여 PGO에 프로필을 사용할 수 있습니다.

참고: 코드가 변경되었으므로 Clang에서 프로필 데이터를 더 이상 사용할 수 없으면 -Wprofile-instr-out-of-date 경고가 생성됩니다.

PGO 사용

PGO를 사용하려면 다음 단계를 실행해야 합니다.

  1. -fprofile-generate를 컴파일러와 링커에 전달하여 계측을 사용해 라이브러리/실행 파일을 빌드합니다.
  2. 계측 바이너리에서 대표 워크로드를 실행하여 프로필을 수집합니다.
  3. llvm-profdata 유틸리티를 사용하여 프로필을 후처리합니다. 자세한 내용은 LLVM 프로필 파일 처리를 참고하세요.
  4. -fprofile-use=<>.profdata를 컴파일러와 링커에 전달하여 프로필을 사용해 PGO를 적용합니다.

Android의 PGO의 경우 프로필을 오프라인에서 수집하고 코드와 함께 체크인하여 재현할 수 있는 빌드를 만듭니다. 프로필은 코드가 발전함에 따라 사용할 수 있지만 주기적으로(또는 Clang에서 프로필이 오래되었다고 경고하는 경우) 재생성해야 합니다.

프로필 수집

Clang은 라이브러리의 계측 빌드를 사용해 벤치마크를 실행하거나 벤치마크가 실행된 경우에는 하드웨어 카운터를 샘플링하여 수집된 프로필을 사용할 수 있습니다. 현재 Android는 샘플링 기반 프로필 수집 사용을 지원하지 않으므로 계측 빌드를 사용하여 프로필을 수집해야 합니다.

  1. 벤치마크를 식별하고 식별된 벤치마크에서 집합적으로 실행한 라이브러리 세트를 식별합니다.
  2. pgo 속성을 벤치마크 및 라이브러리에 추가합니다(자세한 내용은 아래 참고).
  3. 다음을 사용하여 이러한 라이브러리의 계측 사본으로 Android 빌드를 생성합니다.
    make ANDROID_PGO_INSTRUMENT=benchmark

benchmark는 빌드 중에 계측된 라이브러리의 컬렉션을 식별하는 자리표시자입니다. 실제 대표 입력(및 벤치마크 대상 라이브러리와 연결되는 다른 실행 파일)은 PGO에 한정되지 않으며 이 문서에서 설명하는 범위를 벗어납니다.

  1. 기기에서 계측된 빌드를 플래시 또는 동기화합니다.
  2. 벤치마크를 실행하여 프로필을 수집합니다.
  3. 아래에서 설명하는 llvm-profdata 도구를 사용하여 프로필을 후처리하고 소스 트리에 체크인할 준비를 합니다.

빌드 중 프로필 사용

Android 트리에서 프로필을 toolchain/pgo-profiles로 체크인합니다. 이름은 라이브러리의 pgo 속성의 profile_file 하위 속성에서 지정된 이름과 일치해야 합니다. 빌드 시스템은 라이브러리를 빌드할 때 프로필 파일을 Clang에 자동으로 전달합니다. PGO를 일시적으로 사용 중지하고 성능 이점을 측정하려면 ANDROID_PGO_DISABLE_PROFILE_USE 환경 변수를 true로 설정하면 됩니다.

추가 제품별 프로필 디렉터리를 지정하려면 BoardConfig.mk에서 PGO_ADDITIONAL_PROFILE_DIRECTORIES make 변수에 추가합니다. 추가 경로를 지정하면 이러한 경로의 프로필이 toolchain/pgo-profiles의 프로필을 재정의합니다.

makedist 타겟을 사용하여 출시 이미지를 생성할 때 빌드 시스템은 누락된 프로필 파일의 이름을 $DIST_DIR/pgo_profile_file_missing.txt에 씁니다. 이 파일을 확인하여 실수로 삭제한 프로필 파일을 확인할 수 있습니다. 프로필 파일이 삭제되면 PGO가 자동으로 사용 중지됩니다.

Android.bp 파일에서 PGO 사용 설정

네이티브 모듈의 Android.bp 파일에서 PGO를 사용 설정하려면 pgo 속성을 지정하기만 하면 됩니다. 이 속성에는 다음과 같은 하위 속성이 있습니다.

속성 설명
instrumentation 계측을 사용하는 PGO의 경우 true로 설정합니다. 기본값은 false입니다.
sampling 샘플링을 사용하는 PGO의 경우 true로 설정합니다. 기본값은 false입니다.
benchmarks 문자열 목록. 목록의 벤치마크가 ANDROID_PGO_INSTRUMENT 빌드 옵션에 지정된 경우 이 모듈은 프로파일링을 위해 빌드됩니다.
profile_file PGO에서 사용할 프로필 파일(toolchain/pgo-profile 기준). 빌드는 enable_profile_use 속성이 false로, 또는 ANDROID_PGO_NO_PROFILE_USE 빌드 변수가 true로 설정되지 않은 한 이 파일을 $DIST_DIR/pgo_profile_file_missing.txt에 추가하여 이 파일이 없다고 경고합니다.
enable_profile_use 빌드 중에 프로필을 사용하면 안 되는 경우 false로 설정합니다. 프로필 수집을 사용 설정하거나 PGO를 일시적으로 사용 중지하기 위해 부트스트랩하는 동안 사용할 수 있습니다. 기본값은 true입니다.
cflags 계측 빌드 중 사용할 추가 플래그의 목록입니다.

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",
    }
}

벤치마크 benchmark1benchmark2가 라이브러리 libstatic1, libstatic2 또는 libshared1의 대표 동작을 실행하면 이러한 라이브러리의 pgo 속성에도 벤치마크가 포함될 수 있습니다. Android.bpdefaults 모듈에는 여러 모듈에 동일한 빌드 규칙을 반복하지 않도록 라이브러리 세트의 공통 pgo 사양이 포함될 수 있습니다.

다른 프로필 파일을 선택하거나 아키텍처의 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",
              }
         }
    }
}

계측 기반 프로파일링 중에 프로파일링 런타임 라이브러리 참조를 확인하려면 빌드 플래그 -fprofile-generate를 링커에 전달합니다. PGO로 계측된 정적 라이브러리, 모든 공유 라이브러리, 정적 라이브러리에 직접 의존하는 모든 바이너리도 PGO에 맞게 계측되어야 합니다. 그러나 이러한 공유 라이브러리나 실행 파일에는 PGO 프로필을 사용할 필요가 없으므로 enable_profile_use 속성을 false로 설정할 수 있습니다. 이러한 제한을 제외하면 PGO는 모든 정적 라이브러리, 공유 라이브러리 또는 실행 파일에 적용할 수 있습니다.

LLVM 프로필 파일 처리

계측 라이브러리 또는 실행 파일을 실행하면 /data/local/tmpdefault_unique_id_0.profraw라는 프로필 파일이 생성됩니다. 여기서 unique_id는 이 라이브러리에 고유한 숫자 해시입니다. 이 파일이 이미 존재하는 경우 프로파일링 런타임은 프로필을 쓰는 동안 새 프로필을 이전 프로필과 병합합니다. 앱 개발자는 /data/local/tmp에 액세스할 수 없습니다. 대신 /storage/emulated/0/Android/data/packagename/files 같은 위치를 사용해야 합니다. 프로필 파일의 위치를 변경하려면 런타임 시 LLVM_PROFILE_FILE 환경 변수를 설정하세요.

그런 다음 llvm-profdata 유틸리티를 사용하여 .profraw 파일을 .profdata 파일로 변환(여러 .profraw 파일을 병합할 수도 있음)합니다.

  llvm-profdata merge -output=profile.profdata <.profraw and/or .profdata files>

그러면 profile.profdata가 소스 트리에 체크인되어 빌드 중에 사용할 수 있습니다.

벤치마크 중에 여러 계측 바이너리/라이브러리가 로드된 경우 각 라이브러리는 개별 고유 ID를 가진 별도의 .profraw 파일을 생성합니다. 일반적으로 이러한 파일은 모두 단일 .profdata 파일로 병합하여 PGO 빌드에 사용할 수 있습니다. 라이브러리가 다른 벤치마크에서 실행되는 경우 두 벤치마크의 프로필을 사용하여 라이브러리를 최적화해야 합니다. 이때 llvm-profdatashow 옵션이 유용합니다.

  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를 검색합니다.

우수사례: ART의 PGO

이 우수사례에서는 ART를 관련 있는 예로 제시합니다. 그러나 이 예는 ART 또는 그 상호 의존성에 프로파일링된 실제 라이브러리 세트에 관한 정확한 설명은 아닙니다.

ART의 dex2oat 사전 컴파일러는 libart-compiler.so에 종속되므로 결과적으로 libart.so에 종속됩니다. ART 런타임은 주로 libart.so에서 구현됩니다. 컴파일러 및 런타임에 관한 벤치마크는 다음과 같이 다릅니다.

벤치마크 프로파일링된 라이브러리
dex2oat dex2oat(실행 파일), libart-compiler.so, libart.so
art_runtime libart.so
  1. 다음 pgo 속성을 dex2oat, libart-compiler.so에 추가합니다.
        pgo: {
            instrumentation: true,
            benchmarks: ["dex2oat",],
            profile_file: "dex2oat.profdata",
        }
  2. 다음 pgo 속성을 libart.so에 추가합니다.
        pgo: {
            instrumentation: true,
            benchmarks: ["art_runtime", "dex2oat",],
            profile_file: "libart.profdata",
        }
  3. 다음을 사용하여 dex2oatart_runtime 벤치마크의 계측 빌드를 만듭니다.
        make ANDROID_PGO_INSTRUMENT=dex2oat
        make ANDROID_PGO_INSTRUMENT=art_runtime
  4. 또는 다음을 사용하여 모든 라이브러리가 계측된 단일 계측 빌드를 만듭니다.

        make ANDROID_PGO_INSTRUMENT=dex2oat,art_runtime
        (or)
        make ANDROID_PGO_INSTRUMENT=ALL

    두 번째 명령어는 프로파일링을 위해 모든 PGO 사용 모듈을 빌드합니다.

  5. dex2oatart_runtime을 실행하는 벤치마크를 실행하여 다음을 얻습니다.
    • LLVM 프로필 파일 처리에서 설명한 메서드를 사용하여 식별된 dex2oat.profraw 파일 세 개(dex2oat_exe.profdata, dex2oat_libart-compiler.profdata, dexeoat_libart.profdata)
    • 단일 art_runtime_libart.profdata
  6. 다음을 사용하여 dex2oat 실행 파일과 libart-compiler.so의 공통 profdata 파일을 생성합니다.
    llvm-profdata merge -output=dex2oat.profdata \
        dex2oat_exe.profdata dex2oat_libart-compiler.profdata
  7. 두 벤치마크의 프로필을 병합하여 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의 프로필에 두 배의 가중치를 할당합니다. 실제 가중치는 도메인 지식 또는 실험을 기준으로 결정해야 합니다.

  8. 프로필 파일 dex2oat.profdatalibart.profdatatoolchain/pgo-profiles로 체크인하여 빌드 중에 사용합니다.